C++ プログラミング

プログラミングで出逢った出来事

dirent の複製で EXC_BAD_ACCESS が発生する

C++ を混ぜて作成していた OS X アプリが、実行時に「ときどき落ちる」エラーに見舞われました。

エラーの原因はEXC_BAD_ACCESS のようで、エラーの場所はだいたいが C++ のstd::ostrstreamstd::string のデータを書き込んでいるときのstd::ostrstream 内でのエラーのようでした。ただ、別のところでエラーが発生することもあり、エラー自体が発生したりしなかったりだったため、ぱっと見で原因はよくわかりません。

ただ、このような掴みづらいエラーのときは C++ のメモリ確保まわりでミスがあることが多いような気がします。

メモリまわりのデバッグ機能を有効化

メモリ確保まわりでエラーがあるとすれば、もしかするとXcode でメモリまわりのデバッグ機能を有効にすれば何か判るかもしれないと思い、Run アクションのDiagnostics 設定を調整してみることにしました。

ここにあるMemory Management カテゴリのEnable Guard Malloc にチェックをいれてアプリを実行してみたところ、ディレクトリエントリの一覧を取得するscandir 関数で取得したdirent の値を代入演算子でコピーしようとしたところでEXC_BAD_ACCESS エラーが発生するようになりました。

dirent dstEntry = *srcEntryPtr;

代入方法を要素毎のコピーに変更

構造体と言えば、代入演算子で中の要素がそのままコピーできそうにも思えますが、このdirent 構造体の場合は、要素のd_name が文字の配列のためか、代入演算子では上手くコピーできないようです。

そこで次のように、要素をひとつひとつ代入して、d_name の値だけはstrcpy 関数でコピーするようにしてみました。

dirent& direntCopy(dirent& dstEntry, const dirent& srcEntry)
{
	dstEntry.d_ino = srcEntry.d_ino;
	strcpy(dstEntry.d_name, srcEntry.d_name);
	dstEntry.d_namlen = srcEntry.d_namlen;
	dstEntry.d_reclen = srcEntry.d_reclen;
	dstEntry.d_seekoff = srcEntry.d_seekoff;
	dstEntry.d_type = srcEntry.d_type;
	
	return dstEntry;
}

そうしたところ、EXC_BAD_ACCESS エラーは発生しなくなりました。

内容を丸ごとコピーするなら、構造体全体を memcpy で書き換えることでも対応できるかと思ったのですが、そのようにしても EXC_BAD_ACCESS は解消されませんでした。理解しきれていないのですけど、 d_name が固定長配列でのアドレスがおかしくなるのでしょうか。