改行がフォーマット文字列にない限り、呼び出しの後にprintfがフラッシュしないのはなぜですか?


539

printf改行がフォーマット文字列にない限り、呼び出し後にフラッシュしないのはなぜですか?これはPOSIXの動作ですか?printf毎回すぐにフラッシュするにはどうすればよいですか?


2
これがファイルで発生するのか、端末でのみ発生するのかを調査しましたか?これは、バックグラウンドプログラムから未完成の行を出力しないという賢い端末機能のように思えますがフォアグラウンドプログラムには当てはまらないと思います。
PypeBros 2009年

7
Cygwin bashの下で、改行フォーマット文字列にある場合でも、これと同じ誤動作が見られます。この問題はWindows 7で新たに発生しました。同じソースコードがWindows XPで正常に動作しました。MS cmd.exeが期待どおりにフラッシュします。修正setvbuf(stdout, (char*)NULL, _IONBF, 0)は問題を回避しますが、確かに必要ではなかったはずです。MSVC ++ 2008 Expressを使用しています。~~~
スティーブピッチャーズ、

9
質問のタイトルを明確にするために、それ自体はprintf(..) フラッシュを行わない、それstdoutは改行を見たときにフラッシュする可能性のあるバッファリングです(ラインがバッファリングされている場合)。これはと同じように反応するputchar('\n');ためprintf(..)、この点で特別なことではありません。これはとは対照的です。cout << endl;そのドキュメントでは、フラッシュについて顕著に言及しています。printfドキュメントでは、フラッシュについてはまったく触れられていません。
Evgeni Sergeev

1
書き込み(/ flushing)は潜在的にコストのかかる操作であり、パフォーマンス上の理由からおそらくバッファリングされます。
hanshenrik 2017

@EvgeniSergeev:質問によって問題が誤って診断され、出力に改行があるときにフラッシュが発生するというコンセンサスはありますか?(フォーマット文字列に1つを置くことは、出力で1つを取得する唯一の方法ではありません)。
Ben Voigt

回答:


702

stdoutストリームは、デフォルトではバッファリングラインなので、それは改行に到達した後にのみ、バッファに何が表示されます(または、それが言われていますとき)。すぐに印刷するには、いくつかのオプションがあります。

印刷stderrの代わりに使用してfprintfstderrあるデフォルトではバッファなし):

fprintf(stderr, "I will be printed immediately");

使用する必要があるときはいつでもstdoutをフラッシュしますfflush

printf("Buffered, will be flushed");
fflush(stdout); // Will now print everything in the stdout buffer

編集:以下のAndy Rossのコメントから、次を使用してstdoutのバッファリングを無効にすることもできますsetbuf

setbuf(stdout, NULL);

またはここでsetvbuf説明さている安全なバージョン

setvbuf(stdout, NULL, _IONBF, 0); 

266
または、バッファリングを完全に無効にするには:setbuf(stdout, NULL);
Andy Ross

80
また、明らかにUNIXでは、改行は通常、標準出力が端末である場合にのみバッファをフラッシュすることを述べたかっただけです。出力がファイルにリダイレクトされている場合、改行はフラッシュされません。
hora

5
追加する必要があると思います。私はこの理論をテストしているだけでsetlinebuf()、ターミナルに向けられていないストリームで使用すると、各行の終わりでフラッシュすることわかりました。
Doddy

8
「最初に開いたとき、標準エラーストリームは完全にバッファリングされていません。標準入力ストリームと標準出力ストリームは、ストリームがインタラクティブデバイスを参照しないと判断できる場合にのみ完全にバッファリングされます」-次の質問を参照してください:stackoverflow.com / questions / 5229096 /…
Seppo Enarvi

3
@RuddZwolinskiこれが「なぜ印刷しないのか」という良い規範の答えになる場合、「printfは常に改行に遭遇したときにバッファをフラッシュするのですか?」のように端末/ファイルの区別に言及することが重要なようです。直接この非常にupvoted答えでは、人々がコメントを読むために必要と対...
HostileForkがdont信頼SEと言う

128

いいえ、それ POSIXの動作でなく、ISOの動作です(まあ、POSIXの動作ですが、ISOに準拠している場合に限ります)。

標準出力は、インタラクティブデバイスを参照するために検出できる場合はラインバッファリングされ、それ以外の場合は完全にバッファリングされます。したがってprintf、次のように、送信する改行を取得してもフラッシュされない状況があります。

myprog >myfile.txt

ユーザーとやり取りしている場合、ユーザーはおそらくすべての行を確認する必要があるため、これは効率にとって意味があります。出力をファイルに送信する場合は、反対側にユーザ​​ーがいない可能性が高いです(不可能ではありませんが、ファイルをテーリングしている可能性があります)。ここで、ユーザーがすべてのキャラクターを見たいと思うかもしれません、それには2つの問題があります。

1つ目は、あまり効率的ではないということです。2つ目は、元のANSI Cの義務は、新しい動作を発明するのではなく、主に既存の動作をコード化することであり、それらの設計決定はANSIがプロセスを開始するずっと前に行われたということです。ISOでさえ、現在、標準の既存のルールを変更する際には非常に注意深く踏み込んでいます。

これに対処する方法についてはfflush (stdout)、すべての出力呼び出しの後ですぐに確認したい場合は、問題が解決します。

または、をsetvbuf操作する前にを使用してstdout、それをバッファなしに設定fflushし、コードにこれらすべての行を追加することを心配する必要はありません。

setvbuf (stdout, NULL, _IONBF, BUFSIZ);

ちょうどあなたがあれば、パフォーマンスにかなりの影響を与えることを覚えておいてくださいされているファイルに出力を送信します。また、これに対するサポートは実装定義であり、標準では保証されていないことにも注意してください。

ISO C99セクション7.19.3/3は関連ビットです:

ストリームがバッファリングされていない場合、文字はソースから、または宛先にできるだけ早く表示されるようになっています。そうしないと、文字が蓄積され、ホスト環境との間でブロックとして送信されます。

ストリームが完全にバッファリングされている場合、文字は、バッファがいっぱいになったときにブロックとしてホスト環境との間で送受信されることを目的としています。

ストリームがラインバッファリングされている場合、改行文字が検出されると、文字はブロックとしてホスト環境との間で送受信されます。

さらに、バッファがいっぱいになったとき、バッファリングされていないストリームで入力が要求されたとき、またはホスト環境からの文字の送信を必要とするラインバッファリングストリームで入力が要求されたときに、文字はブロックとしてホスト環境に送信されるように意図されています。 。

これらの特性のサポートは実装定義であり、setbufおよびsetvbuf関数を介して影響を受ける可能性があります。


8
「\ n」があっても、printf()がフラッシュしないというシナリオに遭遇しました。ここで述べたように、fflush(stdout)を追加することでそれを克服しました。しかし、私は '\ n'がprintf()でバッファをフラッシュできなかった理由を疑問に思っています。
Qiang Xu

11
@QiangXu、標準出力は、対話型デバイスを参照することを明確に決定できる場合にのみ行バッファリングされます。したがって、たとえば、を使用して出力をリダイレクトするとmyprog >/tmp/tmpfile、ラインバッファリングではなく完全にバッファリングされます。メモリから、標準出力がインタラクティブかどうかの判断は実装に委ねられます。
paxdiablo 2012

3
:さらに、Windows上で_IOLBFとして呼び出すことはsetvbuf(....、_IOLBF)うではない仕事があっ_IOFBFと同じであるmsdn.microsoft.com/en-us/library/86cebhfs.aspx
ピョートルLopusiewicz

28

効率と、1つのTTYに書き込む複数のプログラムがある場合、この方法では1行の文字がインターレースされないため、おそらくそのようになります。したがって、プログラムAとBが出力している場合、通常は次のようになります。

program A output
program B output
program B output
program A output
program B output

これは悪臭を放ちますが、それは

proprogrgraam m AB  ououtputputt
prproogrgram amB A  ououtputtput
program B output

改行でフラッシュすることが保証されていないことに注意してください。フラッシュが重要な場合は、明示的にフラッシュする必要があります。


26

呼び出しをすぐにフラッシュするには、fflush(stdout)またはfflush(NULL)NULLすべてをフラッシュすることを意味します)


31
覚えておいてfflush(NULL);、通常は非常に悪い考えです。多くのファイルを開いている場合は、特にロックを求めてすべてと戦うマルチスレッド環境でパフォーマンスが低下します。
R .. GitHub ICEの

14

注:Microsoftランタイムライブラリは行バッファリングをサポートしていないため、次のようになりprintf("will print immediately to terminal")ます。

https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf


3
より悪いprintf「ノーマル」の場合には、端末にすぐに行くのは事実であるprintffprintfより粗くしても、それらの出力は、すぐに使用に置かれた場合にバッファます。MSが修正していない限り、1つのプログラムが別のプログラムからstderrとstdoutをキャプチャして、それぞれに送信された順序を特定することは不可能です。
スーパーキャット2015

いいえ、バッファリングが設定されていない限り、それはすぐに端末に出力されません。デフォルトでは、完全なバッファリングが使用されます
phuclv

12

stdoutはバッファリングされるため、改行が出力された後にのみ出力されます。

すぐに出力するには、次のいずれかを実行します。

  1. stderrに出力します。
  2. stdoutのバッファリングを解除します。

10
またはfflush(stdout)
ラスタジェダイ2016

2
「したがって、改行が印刷された後にのみ出力されます。」これだけでなく、少なくとも4つのケース。完全な、書き込みをバッファリングしstderr、(この答えは後ほど言及)fflush(stdout)fflush(NULL)
chux-モニカを2017年

11

デフォルトでは、stdoutは行バッファリングされ、stderrはバッファリングされず、ファイルは完全にバッファリングされます。


10

代わりに、バッファリングされていないstderrにfprintfできます。または、必要に応じてstdoutをフラッシュできます。または、標準出力をバッファなしに設定できます。



2

通常、バッファリングには2つのレベルがあります。

1.カーネルバッファキャッシュ(読み取り/書き込みを高速化)

2. I / Oライブラリでのバッファリング(システムコール数を削減)

の例を見てみましょうfprintf and write()

を呼び出しfprintf()ても、ファイルには直接アクセスしません。まず、プログラムのメモリ内のstdioバッファに移動します。そこから、書き込みシステムコールを使用してカーネルバッファキャッシュに書き込まれます。したがって、I / Oバッファをスキップする1つの方法は、直接write()を使用することです。他の方法は、を使用することsetbuff(stream,NULL)です。これにより、バッファリングモードがバッファリングなしに設定され、データはカーネルバッファに直接書き込まれます。データを強制的にカーネルバッファーにシフトするには、「\ n」を使用できます。これは、デフォルトのバッファリングモードが「ラインバッファリング」の場合、I / Oバッファーをフラッシュします。またはを使用できますfflush(FILE *stream)

これでカーネルバッファに移動しました。カーネル(/ OS)はディスクアクセス時間を最小限に抑えたいので、ディスクのブロックのみを読み書きします。したがってread()、システムコールであり、直接またはを介して呼び出すことができるが発行されるとfscanf()、カーネルはディスクからディスクブロックを読み取り、それをバッファーに格納します。その後、データはここからユーザー空間にコピーされます。

同様に、fprintf()I / Oバッファーから受け取ったデータは、カーネルによってディスクに書き込まれます。これにより、read()およびwrite()が高速になります。

次に、カーネルにを開始するよう強制しますwrite()。その後、データ転送はハードウェアコントローラーによって制御されますが、いくつかの方法もあります。O_SYNC書き込み呼び出し中に、または同様のフラグを使用できます。またはfsync(),fdatasync(),sync()、カーネルバッファーでデータが利用可能になり次第、カーネルに書き込みを開始させるなど、他の関数を使用することもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.