- ベストアンサー
C言語プログラムで複数のファイルに同時書き込みする際のエラーについて
- 複数のC言語プログラムが一つのファイルに書き込みを行う際、エラーが発生せずにプログラムが停止する現象について質問があります。
- 動作環境はWindows 7のVC++2010コンパイラです。プログラムAは「test.txt」に「@」を無限に書き込み続け、プログラムBは「test.txt」に「_」を書き込む処理を実行します。
- 質問内容は、(1)Aプログラムの実行結果に「NULL」が出力される理由、および(2)AプログラムがBプログラムの実行を待っているかどうかについてです。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
同様の事を経験した事があります。 (以下、既に御存知の事が含まれていたらすみません) > Q.NULLが出力されているのはどうしてか. OS に依存する話かもしれませんが、少なくとも windows ではファイルハンドル毎に「現在の書き込み位置」が管理されています。そして、ファイルが現在どの様な状態になっていようとも「現在の書き込み位置」にデータを書き込もうとします。質問の例では、以下の様な動作になっているのではないでしょうか。 (1) プログラム A が起動し、test.txt を作成する (または、既存のファイルの長さを0にする)。 (初期の「書き込み位置」は、勿論ファイルの先頭。) (2) プログラム A が "位置 X" まで '@' を書き込む (X 文字の @ を出力する) (3) プログラム B が起動し、test.txt の長さを 0 にする。 (4) プログラム A は "位置 X" に新しく '@' を書き込もうとするが、 test.txt の長さが短くなっているので、「長さ X まで拡張」してから '@' を書き込む。 (5) 後は、プログラム B は先頭から順番に文字 '_' を書き込み、 プログラム A は "位置 X" 以降に文字 '@' を書き込む。 プログラム B は "位置 Y" まで書き込んだ所で停止し、 プログラム A は "位置 Z" まで書き込んだ所で停止する。 この過程の (4) の「長さ X まで拡張」の所で 0 (NUL) を fill しているのでしょう。 結果として、"_ _ … _ _ (位置 Y) NUL NUL … NUL NUL (位置 X) @ @ … @ @ (位置 Z)" という内容になるのだと思います。 (3) で長さ 0 にされるのが嫌ならば、fopen の時に "w" ではなくて "r+" で開けば良いです。 "r+" は既存ファイルを読み書き両用で開き、ファイルをクリアしない物です。 ファイルが既存でないかもしれないならば、"a" で開いてから rewind 関数を呼ぶなどすれば良いです。 > Q.AはBが書き込んでいる間,待っていたようだ うーん。質問に記述された結果だけ見れば、特にその様な事があったとは言えない気がします。また、(ファイル排他制御などしていないので) 「書き込みの間待っている」という仕様もないと思います。それから、エラーが出るかどうかについては…エラーが出るという事は無いと思いますが、ファイルの中身が滅茶苦茶になるのでこの様なプログラムに意味があるかは分かりません。。。 但し、それぞれのプログラムのバッファリングによって、文字が実際にファイルに書き込まれるタイミングが遅れる事には注意して下さい。 つまり、プログラム上で fputc を実行しても即座にディスクにデータが書き込まれる訳ではなくて、プログラム内部でデータが或る程度溜まるのを待ってから、まとめて書き出しを実行します。(1文字ごとにディスクと通信していたら時間がかかりますものね!) また、fclose を実行すると、溜めているデータを全部書き出してからファイルを閉じますが、プログラムを強制終了すると、溜めている未書き出しデータが出力されずに終了します。 余談: 参考までに: 「現在の書き込み位置」については ftell 関数 fseek 関数 rewind 関数 「バッファリング」に関しては: fflush 関数, setvbuf 関数
その他の回答 (1)
- xitoaki
- ベストアンサー率35% (36/101)
排他制御をしていなければこのようになるのが不思議ではありません。 OS依存の部分が大きいので、断定は出来ませんが、 「AはBが書き込んでいる間,待って」はいないと思います。 どちらもファイルの先頭にあたる同じアドレスから書き始めていますが、 Aが先に「@」を書き込んだ部分に、Bが後から「_」を上書いていっています。 先にBを止めたので、上書きが途中までしか行われず、 後半にAが書いた部分が残っているだけでしょう。 途中のNULLは、強制終了しているために変なデータが入っているのかもしれません。 テキストで開いているため、NULLに見えるだけで、 NULLではない何らかの制御文字が入っている可能性もあります。 毎回同じようになるのであれば、OSのファイル書き込み処理の手順的なものが 書かれているのでしょう。そうでなければ、たまたま何かゴミが入ったものと思います。 EOFを出力せずファイルをクローズもしていないので、 EOFの扱いがどうなっているか判りませんが (このあたりもOSに依存するものでしょう) Bが書いた領域の直後にEOFが書かれなかった為、結果的にAが書いたものが続いて見えています。
お礼
バイナリエディタで開いた上でNULLでした。 一連の現象はOS依存の未定義の挙動であって、本来は排他制御を行う必要があるということですね。 xitoakiさんの推理になるほどと思いました。 ご回答ありがとうございました。
お礼
なるほど、(4)の時点で¥0で埋められている可能性があるのですね。 Y>XだとNULLが発生しないことになりますね。実験パターン不足ですみません。 納得しました。ありがとうございます。 又、rewindなどといった関数を初めて知りました。 あえて排他制御せずにこの失敗実験のようなことを意図的にやりたいときは、アナザープログラムで追加書き込みオープン+ファイルポインタ先頭化でいけるわけですね。必要になったら活かしていこうと思います。 バッファーのサイズも指定できるんですか。 いつもデバッグでプリント文挟んで、そのプリント文を通過しているにもかかわらず出力されず、結局exit関数でエラーの位置を特定するはめになる理由もこれかもしれないですね。出力バッファのサイズを1文字にすればこれを回避できそうです。効率が悪くなるのはありますけれども。 貴重なお話をありがとうございました。
補足
xitoaki様、akinomyoga様、ご回答ありがとうございました。 返信が遅れてすみません。 OSが勝手に排他制御をやってくれているわけではないと分かったので、排他制御をいれていこうと思います。 お二方とも、ありがとうございました。