web-dev-qa-db-ja.com

fflush(stdin)を使用する

したがって、入力バッファをクリアするためのfflush(stdin)の簡単なGoogle検索は、それを使用することに対する多くのWebサイト警告を明らかにします。それでも、それはまさに私のCS教授がクラスにそれを行う方法を教えた方法です。

fflush(stdin)の使用はどれほど悪いですか?教授が使用していて、問題なく動作しているように見えても、実際に使用を控えるべきですか?

66
wrongusername

シンプル:fflushは出力ストリームで呼び出されるため、これは未定義の動作です。これは、C標準からの抜粋です。

int fflush(FILE * ostream);

ostreamは、最新の操作が入力されなかった出力ストリームまたは更新ストリームを指します。fflush関数により、そのストリームの未書き込みデータがホスト環境に配信され、ファイルに書き込まれます。それ以外の場合、動作は未定義です。

ですから、これは「どれほど悪い」の問題ではありません。 fflush(stdin)明らかに間違っているであり、あなたはこれを決して使用しないでくださいです。

65
Eli Bendersky

コメントを回答に変換し、問題が定期的に再発するため、コメントを拡張します。

標準CおよびPOSIXはfflush(stdin)を未定義の動作のままにします

[〜#〜] posix [〜#〜]fflush()のCおよびC++標準は、動作が未定義であると明示的に述べていますが、いずれもシステムによる定義を妨げません。 。

ISO/IEC 9899:2011 — C11標準—言う:

§7.21.5.2fflush関数

¶2streamが最新の操作が入力されていない出力ストリームまたは更新ストリームを指す場合、fflush関数はそのストリームの未書き込みデータをホスト環境に配信しますファイルに書き込まれます。それ以外の場合、動作は未定義です。

POSIXは主にC標準に従いますが、このテキストをC拡張としてマークします。

[CX]読み取り用に開かれたストリームの場合、ファイルがまだEOFになく、ファイルがシーク可能なファイルである場合、開いている基本ファイル記述のファイルオフセットはストリームのファイル位置に設定され、 ungetc()またはungetwc()によってストリームにプッシュバックされ、その後ストリームから読み取られなかった文字は破棄されます(ファイルオフセットをさらに変更することなく)。

端末はシークできないことに注意してください。パイプでもソケットでもありません。

Microsoftはfflush(stdin)の動作を定義しています

Microsoft とVisual Studioランタイムは、入力ストリームでのfflush()の動作を定義します。

ストリームが入力用に開いている場合、fflushはバッファーの内容をクリアします。

M.M

Cygwinは、fflush(stdin)が入力をクリアしないかなり一般的なプラットフォームの例です。

これが、この応答バージョンの comment が「Microsoft and the Visual Studio runtime」に注意する理由です。Microsoft以外のCランタイムライブラリを使用する場合、表示される動作はそのライブラリによって異なります。

Linuxのドキュメントとプラクティスは互いに矛盾しているようです

驚くべきことに、 Linuxfflush(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)を使用しません。マイクロソフトのプラットフォームに関連付けられているコードはそれを使用することができ、動作しますが、移植性の問題に注意してください。

ファイル記述子から未読の端末入力を破棄するPOSIXの方法

stdinのようなファイルストリームとは対照的に)ターミナルファイル記述子から未読情報を破棄するPOSIX標準の方法を nixシステムのtty入力キューから未読データをフラッシュするにはどうすればよいですか) 。ただし、それは標準のI/Oライブラリレベルを下回っています。

34

標準によれば、fflushは出力バッファでのみ使用でき、明らかにstdinはそうではありません。ただし、 some 標準Cライブラリでは、fflush(stdin)を拡張機能として使用できます。その場合、それを使用できますが、移植性に影響するため、地球上の標準準拠の標準Cライブラリを使用できなくなり、同じ結果を期待できなくなります。

18
tiftik

[〜#〜] posix [〜#〜] からの引用:

読み取り用に開いたストリームの場合、ファイルがまだEOFになく、ファイルがシーク可能なファイルである場合、基礎となる開いているファイル記述のファイルオフセットは、ストリームのファイル位置に設定され、すべての文字がプッシュバックされます。 ungetc()またはungetwc()によってストリームに読み込まれた後、ストリームから読み取られなかったものは破棄されます(ファイルオフセットをさらに変更することなく)。

端末はシークできないことに注意してください。

1
Ryan Chen

スペースを含む入力文字列を取得している間、バッファは次の入力ではクリアされず、同じ入力の前の入力を考慮します。これを解決するには、fflush(stdin)を使用してストリーム/バッファをクリアします。

C標準に従って、未定義の動作です。ただし、Microsoft Visual Studioなどの一部のコンパイラでは許可されています。

0
Shubham Kumar