したがって、入力バッファをクリアするためのfflush(stdin)
の簡単なGoogle検索は、それを使用することに対する多くのWebサイト警告を明らかにします。それでも、それはまさに私のCS教授がクラスにそれを行う方法を教えた方法です。
fflush(stdin)
の使用はどれほど悪いですか?教授が使用していて、問題なく動作しているように見えても、実際に使用を控えるべきですか?
シンプル:fflush
は出力ストリームで呼び出されるため、これは未定義の動作です。これは、C標準からの抜粋です。
int fflush(FILE * ostream);
ostreamは、最新の操作が入力されなかった出力ストリームまたは更新ストリームを指します。fflush関数により、そのストリームの未書き込みデータがホスト環境に配信され、ファイルに書き込まれます。それ以外の場合、動作は未定義です。
ですから、これは「どれほど悪い」の問題ではありません。 fflush(stdin)
は明らかに間違っているであり、あなたはこれを決して使用しないでくださいです。
コメントを回答に変換し、問題が定期的に再発するため、コメントを拡張します。
fflush(stdin)
を未定義の動作のままにします[〜#〜] posix [〜#〜] 、fflush()
のCおよびC++標準は、動作が未定義であると明示的に述べていますが、いずれもシステムによる定義を妨げません。 。
ISO/IEC 9899:2011 — C11標準—言う:
§7.21.5.2fflush関数
¶2
stream
が最新の操作が入力されていない出力ストリームまたは更新ストリームを指す場合、fflush
関数はそのストリームの未書き込みデータをホスト環境に配信しますファイルに書き込まれます。それ以外の場合、動作は未定義です。
POSIXは主にC標準に従いますが、このテキストをC拡張としてマークします。
[CX]読み取り用に開かれたストリームの場合、ファイルがまだEOFになく、ファイルがシーク可能なファイルである場合、開いている基本ファイル記述のファイルオフセットはストリームのファイル位置に設定され、
ungetc()
またはungetwc()
によってストリームにプッシュバックされ、その後ストリームから読み取られなかった文字は破棄されます(ファイルオフセットをさらに変更することなく)。
端末はシークできないことに注意してください。パイプでもソケットでもありません。
fflush(stdin)
の動作を定義していますMicrosoft とVisual Studioランタイムは、入力ストリームでのfflush()
の動作を定義します。
ストリームが入力用に開いている場合、
fflush
はバッファーの内容をクリアします。
Cygwinは、
fflush(stdin)
が入力をクリアしないかなり一般的なプラットフォームの例です。
これが、この応答バージョンの comment が「Microsoft and the Visual Studio runtime」に注意する理由です。Microsoft以外のCランタイムライブラリを使用する場合、表示される動作はそのライブラリによって異なります。
驚くべきことに、 Linux はfflush(stdin)
の動作も名目上文書化しており、同じように定義しています(奇跡の奇跡)。
入力ストリームの場合、
fflush()
は、基礎となるファイルからフェッチされたが、アプリケーションによって消費されていないバッファリングされたデータを破棄します。
fflush(stdin)
が機能すると言っているLinuxのドキュメントには、少し戸惑っています。その提案にもかかわらず、ほとんどの場合Linuxでは機能しません。 Ubuntu 14.04 LTSのドキュメントを確認しました。上記で引用されていることを示していますが、経験的には動作しません。少なくとも、入力ストリームが端末などのシークできないデバイスの場合は。
demo-fflush.c
__#include <stdio.h>
int main(void)
{
int c;
if ((c = getchar()) != EOF)
{
printf("Got %c; enter some new data\n", c);
fflush(stdin);
}
if ((c = getchar()) != EOF)
printf("Got %c\n", c);
return 0;
}
_
_$ ./demo-fflush
Alliteration
Got A; enter some new data
Got l
$
_
この出力は、Ubuntu 14.04 LTSとMac OS X 10.11.2の両方で取得されました。私の理解では、Linuxのマニュアルに書かれていることと矛盾しています。 fflush(stdin)
操作が機能する場合、2番目のgetchar()
が読み取る情報を取得するには、新しいテキスト行を入力する必要があります。
POSIX標準が述べていることを考えると、おそらくより良いデモンストレーションが必要であり、Linuxのドキュメントを明確にする必要があります。
demo-fflush2.c
__#include <stdio.h>
int main(void)
{
int c;
if ((c = getchar()) != EOF)
{
printf("Got %c\n", c);
ungetc('B', stdin);
ungetc('Z', stdin);
if ((c = getchar()) == EOF)
{
fprintf(stderr, "Huh?!\n");
return 1;
}
printf("Got %c after ungetc()\n", c);
fflush(stdin);
}
if ((c = getchar()) != EOF)
printf("Got %c\n", c);
return 0;
}
_
_/etc/passwd
_はシーク可能なファイルであることに注意してください。 Ubuntuでは、最初の行は次のようになります。
_root:x:0:0:root:/root:/bin/bash
_
Mac OS Xでは、最初の4行は次のようになります。
_##
# User Database
#
# Note that this file is consulted directly only when the system is running
_
言い換えれば、Mac OS Xの_/etc/passwd
_ファイルの上部にコメントがあります。非コメント行は通常のレイアウトに準拠しているため、root
エントリは次のとおりです。
_root:*:0:0:System Administrator:/var/root:/bin/sh
_
Ubuntu 14.04 LTS:
_$ ./demo-fflush2 < /etc/passwd
Got r
Got Z after ungetc()
Got o
$ ./demo-fflush2
Allotrope
Got A
Got Z after ungetc()
Got B
$
_
Mac OS X 10.11.2:
_$ ./demo-fflush2 < /etc/passwd
Got #
Got Z after ungetc()
Got B
$
_
Mac OS Xの動作はfflush(stdin)
を無視します(少なくとも無視するようです)(したがって、この問題でPOSIXをフォローしていません)。 Linuxの動作は、文書化されたPOSIXの動作に対応していますが、POSIX仕様では、シーク可能なファイルが指定されていますが、ターミナルはシークをサポートしていません。また、Microsoftの仕様よりもはるかに有用ではありません。
Microsoftは、fflush(stdin)
の動作を文書化しています。どうやら、ネイティブのWindowsコンパイラとCランタイムサポートライブラリを使用して、Windowsプラットフォームで文書化されているように動作します。
それとは反対にドキュメントがありますが、標準入力が端末の場合、Linuxでは機能しませんが、POSIX仕様に準拠しているようです。 C標準によれば、fflush(stdin)
の動作は未定義です。 POSIXは、「入力ファイルがシーク可能でない限り」修飾子を追加しますが、ターミナルはそうではありません。動作はマイクロソフトの動作と同じではありません。
したがって、移植可能なコードはfflush(stdin)
を使用しません。マイクロソフトのプラットフォームに関連付けられているコードはそれを使用することができ、動作しますが、移植性の問題に注意してください。
(stdin
のようなファイルストリームとは対照的に)ターミナルファイル記述子から未読情報を破棄するPOSIX標準の方法を nixシステムのtty入力キューから未読データをフラッシュするにはどうすればよいですか) 。ただし、それは標準のI/Oライブラリレベルを下回っています。
標準によれば、fflush
は出力バッファでのみ使用でき、明らかにstdin
はそうではありません。ただし、 some 標準Cライブラリでは、fflush(stdin)
を拡張機能として使用できます。その場合、それを使用できますが、移植性に影響するため、地球上の標準準拠の標準Cライブラリを使用できなくなり、同じ結果を期待できなくなります。
[〜#〜] posix [〜#〜] からの引用:
読み取り用に開いたストリームの場合、ファイルがまだEOFになく、ファイルがシーク可能なファイルである場合、基礎となる開いているファイル記述のファイルオフセットは、ストリームのファイル位置に設定され、すべての文字がプッシュバックされます。 ungetc()またはungetwc()によってストリームに読み込まれた後、ストリームから読み取られなかったものは破棄されます(ファイルオフセットをさらに変更することなく)。
端末はシークできないことに注意してください。
スペースを含む入力文字列を取得している間、バッファは次の入力ではクリアされず、同じ入力の前の入力を考慮します。これを解決するには、fflush(stdin)を使用してストリーム/バッファをクリアします。
C標準に従って、未定義の動作です。ただし、Microsoft Visual Studioなどの一部のコンパイラでは許可されています。