とあるC++ソースの不具合を調査をしていたところ、おかしな動きをしている箇所があった。
サンプルソース
// 変数宣言
char strA[] = "12345678";
char strB[8];
char ret[16];
// 初期化
memset(strB, 0x00, sizeof(strB));
memset(ret, 0x00, sizeof(ret));
// "12345678"をchar[8]の配列にコピー
memcpy(strB, strA, sizeof(strB));
// char[8]の内容をシングルクォーテーションで囲ってchar[16]の配列へ
sprintf(ret, "'%s'", strB);
// char[16]の配列を返す
return ret;
こんな感じのソースで、retの値に「’12345678’」が入ることを期待していたんだけど、変な値で返ってくる…。
サンプルソースで書いてしまえば一目瞭然なんだけど、実際のソースはもっと複雑なのと、SDLチェックも外されていたので、原因に気付くまで時間がかかった。
原因は文字列の終端文字分のサイズが考慮されていないこと
C言語やC++では、文字列に「終端文字」と呼ばれる「\0」(ヌル文字)を最後に付ける必要がある。
なので、例えば最大8文字入る変数は、プラス終端文字分でサイズを9用意する必要がある。
そうしないと、文字列の終端が捉えられず、正しいデータを取得できなくなってしまう。
ちなみに、上記サンプルソースのうち、
char strA[] = "12345678";
のように、サイズを省略して指定した場合、9扱いとなる。