2つのプログラムで一つのテキストファイルに書き込むと,エラーが起きてプログラムが停止するんじゃないかなーと思っていましたが,起きませんでした.
検証結果がまた謎であり,知識も足りず,解釈ができないので,力を貸してください.
動作環境は先に言いますと,win7のVC++2010コンパイラです.
---プログラムA
FILE *fp = fopen("test.txt", "w");
while(true){
fputc('@', fp);
}
fclose(fp);
---end
---プログラムB
FILE *fp = fopen("test.txt", "w");
while(true){
fputc('_', fp);
}
fclose(fp);
---end
(1)Aを走らせる・・・ファイルには@がたくさん出力されていると思われる.
(2)Bを走らせる・・・Aがファイルを操作しているんだから,fopen関数でエラーが出ると思ったが,出なかった.稼働してしまったので,fopenの書き込みモードによってファイルは真っ新になり,「_」が先頭から羅列され始めただろう.
(3)Bを停止する・・・「_」の出力は止まっただろうが,プログラムAの方は今何をしているんだろう.元気かな
(4)Aを停止する
出力結果すなわちtest.txtの内容は次のようだった.
___________・・・______[NULL][NULL][NULL]・・・[NULL][NULL]@@@@@@@・・・@@@@@@@
これについて質問があります.
Q.NULLが出力されているのはどうしてか.
Q.AはBが書き込んでいる間,待っていたようだ(Bを停止した後で待ってましたと言わんばかりに@が書き込まれた形跡があるから)が,これは仕様なのか.エラーが出るということを気にする必要はないのか.
余談,予備知識大歓迎です.よろしくお願い致します.
No.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 関数
この回答への補足
xitoaki様、akinomyoga様、ご回答ありがとうございました。
返信が遅れてすみません。
OSが勝手に排他制御をやってくれているわけではないと分かったので、排他制御をいれていこうと思います。
お二方とも、ありがとうございました。
なるほど、(4)の時点で¥0で埋められている可能性があるのですね。
Y>XだとNULLが発生しないことになりますね。実験パターン不足ですみません。
納得しました。ありがとうございます。
又、rewindなどといった関数を初めて知りました。
あえて排他制御せずにこの失敗実験のようなことを意図的にやりたいときは、アナザープログラムで追加書き込みオープン+ファイルポインタ先頭化でいけるわけですね。必要になったら活かしていこうと思います。
バッファーのサイズも指定できるんですか。
いつもデバッグでプリント文挟んで、そのプリント文を通過しているにもかかわらず出力されず、結局exit関数でエラーの位置を特定するはめになる理由もこれかもしれないですね。出力バッファのサイズを1文字にすればこれを回避できそうです。効率が悪くなるのはありますけれども。
貴重なお話をありがとうございました。
No.1
- 回答日時:
排他制御をしていなければこのようになるのが不思議ではありません。
OS依存の部分が大きいので、断定は出来ませんが、
「AはBが書き込んでいる間,待って」はいないと思います。
どちらもファイルの先頭にあたる同じアドレスから書き始めていますが、
Aが先に「@」を書き込んだ部分に、Bが後から「_」を上書いていっています。
先にBを止めたので、上書きが途中までしか行われず、
後半にAが書いた部分が残っているだけでしょう。
途中のNULLは、強制終了しているために変なデータが入っているのかもしれません。
テキストで開いているため、NULLに見えるだけで、
NULLではない何らかの制御文字が入っている可能性もあります。
毎回同じようになるのであれば、OSのファイル書き込み処理の手順的なものが
書かれているのでしょう。そうでなければ、たまたま何かゴミが入ったものと思います。
EOFを出力せずファイルをクローズもしていないので、
EOFの扱いがどうなっているか判りませんが
(このあたりもOSに依存するものでしょう)
Bが書いた領域の直後にEOFが書かれなかった為、結果的にAが書いたものが続いて見えています。
バイナリエディタで開いた上でNULLでした。
一連の現象はOS依存の未定義の挙動であって、本来は排他制御を行う必要があるということですね。
xitoakiさんの推理になるほどと思いました。
ご回答ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
- ・ゆるやかでぃべーと タイムマシンを破壊すべきか。
- ・歩いた自慢大会
- ・許せない心理テスト
- ・字面がカッコいい英単語
- ・これ何て呼びますか Part2
- ・人生で一番思い出に残ってる靴
- ・ゆるやかでぃべーと すべての高校生はアルバイトをするべきだ。
- ・初めて自分の家と他人の家が違う、と意識した時
- ・単二電池
- ・チョコミントアイス
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
pcap形式データをテキストへ抽出
-
アクセス>マクロ>テキスト変換
-
Acccess レポートをグループ別...
-
VC++でUTF-8のファイルを出力し...
-
外国語とCSVについて
-
C言語による10進数→16進数変換...
-
Excel のページを Jpegファイル...
-
ファイル出力の場所を指定
-
CreateProcess関数と実行後の戻...
-
BitBltについて。
-
I/Oエラー(Delphi)
-
Wordマクロで指定したフォルダ...
-
Excel VBA ファイル一覧とファ...
-
OCRで起こしたテキスト文字をCS...
-
CSV形式に変換
-
UNIX & Linux の標準出力で得た...
-
ファイル形式またはファイル拡...
-
VBA でメモ帳へ保存する際の保...
-
コマンド(例えばls)の出力結果...
-
高速置換の方法
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
【ExcelVBA】UTF-8(BOM無)でC...
-
コマンド(例えばls)の出力結果...
-
Acccess レポートをグループ別...
-
ファイル出力の場所を指定
-
VC++でUTF-8のファイルを出力し...
-
ファイルの文字コードをUTF-8に...
-
CreateProcess関数と実行後の戻...
-
VBA でメモ帳へ保存する際の保...
-
テキストファイルに改行コード...
-
シェルコマンドの 2>&1 とはど...
-
外国語とCSVについて
-
ファイル形式またはファイル拡...
-
AccessVBA複数レポート条件毎に...
-
printfだと出力されるのにfprin...
-
1行ずつではなくまとめてファイ...
-
pcap形式データをテキストへ抽出
-
C言語のプログラムが異常終了し...
-
Wordマクロで指定したフォルダ...
-
CBool関数について VB6とVB.net...
-
C言語での印刷方法
おすすめ情報