基本事項
以前から使い続けてきたC++ソースコードをデバッグしてて気付いたことです。
次のソースコードを見てください。
int main()
{
FILE* fp = fopen("test.txt", "w");
fputs("Hello.", fp);
fclose(fp);
fclose(fp); // 成功?
FILE* fp2 = NULL;
fclose(fp2); // 失敗
}
fclose(fp)というのが2行あります。C++の言語仕様としては「閉じたファイルポインタを使用してはいけない」という決まりなので、本来は不正な処理です。しかし、VC++でビルドしたバイナリを実行すると、特にエラーなどにはならないので、一見問題がないように見えてしまいます。
(他の処理系では未確認です。)
fclose(fp2)は、fp2にNULLが入っているので実行するとエラーになります。
別のファイルを開いていたら?
先ほどのコードを修正し、次のようにしたらどのような動作になるでしょうか?
int main()
{
FILE* fp = fopen("test.txt", "w");
fputs("Hello.", fp);
fclose(fp);
FILE* fp2 = fopen("test2.txt", "w");
fclose(fp); // 成功?
fputs("Hello.", fp2);
fclose(fp2);
}
実は2回目のfputs()で、fp2のファイルポインタにアクセスできないというエラーになります。
fp2だけを見ると、開く→書き込む→閉じるのセットになっていて問題ないように見えるのですが、間にあるfclose(fp)がなんとfp2のファイルを閉じてしまうので、そんな現象になるのです。
上のコードのように明らかに余計なfclose()が書いてあるのがわかる状態なら、とりあえず消してしまえばいいのですが、実際に遭遇したのはこれが以下のような実装になっていて気付くのに時間がかかりました。
- ファイルポインタをクラスが管理していて、公開しているclose()関数とデストラクタ内でそれぞれfclose()を実行(念のため?)
- このクラス自体は複数のファイルを開くことはないが、複数のスレッドが同時に実行する
デバッグしていると、2スレッドくらいなら問題が発生しにくいのですが、10スレッドくらいだと、今開いたファイルが次の瞬間には閉じていているという不思議な動作になります。