printf と va_args | Chandler@Berlin

Chandler@Berlin

ベルリン在住

size_t と printf の問題の話を前回したが,この問題をもう少し考えてみると,va_args に起因することがわかる.va_args ではプログラム側でどんな引数の型をとっているのかをプログラム側で知る必要があるからである.引数のpromotion はおそらくこの問題を緩和しようとして考えられたものであろう.

先日出会ったバグは以下のようなものであった.このバグは 64bit 環境でしか起こらない.

va_args を使った函数として,以下のようなものを考えよう.

---
void vafunction(const char* p_name, ...)
{
va_list ap;
va_start(ap, p_name);
while(p_name != 0){
// do something
p_name = va_arg(ap, const char*);
}
va_end(ap);
}
---

これを,

vafunction("This sometimes doesn't work in 64bit.", 0); // (1)

として呼び出すと,クラッシュすることがある.ただし,常にクラッシュするとは限らない.デバッガで追いかけてみると,クラッシュする場合には,p_name が0 にならず,segmentation fault を起こすのである.しかし,

vafunction("This should always work in 64bit.", NULL); // (2)

ならば大丈夫である.ここでの違いというのは函数の最後の引数が 0 か NULLかというだけである.

これはvafunction 側では 64bit の (const char*)を想定しているにもかかわらず,(1)における 0 は 32bit の (int)0 であるためである.

(2)が問題を起こさないのは NULL が 64bit の (void*)0 として定義されているからである.そのため,このクラッシュは 32bit 環境では発生しない.(1)の場合でも,なんらかの理由でスタックに 0 が余計に入っていた場合にはクラッシュしないことがあるが,それは偶然である.