2006/06/23(金)va_start
64bit FreeBSDのgcc*1において,なぜかvsnprintfが動作しなかったのですが,ようやく原因がなんとなく判明しました.
どうやら,64bit FreeBSDのgccでは,vsnprintfの引数va_listが指しているポインタをいじっている模様.va_listを使う必要がある場合は,毎回va_start,va_endを実行した方が無難そうです.
ダメな例
char *test(const char *format, ...) { int size; int len; char *buf; va_list ap; va_start(ap, format); // ここで ap を初期化 while(1){ buf = (char *)malloc(size); // vsnprintfが複数回呼ばれるが, // apは最初に初期化されたきり len = vsnprintf(buf, size, format, ap); if(0 <= len && len < size) break; if(0 <= len) m_BufferSize = len; else m_BufferSize += BUFFER_SIZE; free(buf); } va_end(ap); return buf; }
安全な例
char *test(const char *format, ...) { int size; int len; char *buf; va_list ap; while(1){ buf = (char *)malloc(size); va_start(ap, format); // ここで ap を初期化 // vsnprintfが複数回呼ばれても, // 毎回きちんとapが初期化されている len = vsnprintf(buf, size, format, ap); va_end(ap); if(0 <= len && len < size) break; if(0 <= len) m_BufferSize = len; else m_BufferSize += BUFFER_SIZE; free(buf); } return buf; }
ちなみに,32bitのLinux*2とFreeBSD*3ではダメな例でも問題ありませんでした.おそらく,va_listに関しての実装が変わっているのでしょう.