(読み取りエラーがない場合)作成者の予想よりも1回だけループに入るので、これは誤りです。読み取りエラーがある場合、ループは終了しません。
次のコードを検討してください。
/* WARNING: demonstration of bad coding technique!! */
#include <stdio.h>
#include <stdlib.h>
FILE *Fopen(const char *path, const char *mode);
int main(int argc, char **argv)
{
FILE *in;
unsigned count;
in = argc > 1 ? Fopen(argv[1], "r") : stdin;
count = 0;
/* WARNING: this is a bug */
while( !feof(in) ) { /* This is WRONG! */
fgetc(in);
count++;
}
printf("Number of characters read: %u\n", count);
return EXIT_SUCCESS;
}
FILE * Fopen(const char *path, const char *mode)
{
FILE *f = fopen(path, mode);
if( f == NULL ) {
perror(path);
exit(EXIT_FAILURE);
}
return f;
}
このプログラムは、入力ストリームの文字数よりも1つ多い値を一貫して出力します(読み取りエラーがない場合)。入力ストリームが空の場合を考えてみましょう:
$ ./a.out < /dev/null
Number of characters read: 1
この場合、feof()
データが読み込まれる前に呼び出されるため、falseを返します。ループに入り、fgetc()
呼び出され(そしてを返しEOF
)、カウントがインクリメントされます。次にfeof()
呼び出されてtrueを返し、ループを中止します。
これは、そのようなすべての場合に発生します。 ストリームの読み取りがファイルの終わりに到達するfeof()
まで、 trueを返しません。の目的はfeof()
、次の読み取りがファイルの終わりに到達するかどうかを確認することではありません。の目的はfeof()
、読み取りエラーとファイルの終わりに達したことを区別することです。fread()
が0を返す場合は、feof
/ ferror
を使用して、エラーが発生したかどうか、またはすべてのデータが消費されたかどうかを判断する必要があります。同様にif fgetc
が返されますEOF
。 freadが0を返した後、またはが戻った後にfeof()
のみ役立ちます。その前に、は常に0を返します。fgetc
EOF
feof()
呼び出す前に、読み取り(fread()
、またはfscanf()
、またはfgetc()
)の戻り値を確認する必要がありますfeof()
。
さらに悪いことに、読み取りエラーが発生した場合を考えてください。その場合、fgetc()
はを返しEOF
、feof()
falseを返し、ループは終了しません。while(!feof(p))
が使用されるすべてのケースで、少なくともforのループ内にチェックがあるferror()
か、少なくともwhile条件が置き換えられるwhile(!feof(p) && !ferror(p))
か、無限ループの非常に現実的な可能性があり、おそらくすべての種類のゴミを無効なデータが処理されています。
つまり、要約すると、「while(!feof(f))
」を書くことが意味的に正しいかもしれない状況が決してないということを確実に述べることはできませんが、読み取りエラーでの無限ループを回避するために、ブレーク付きのループ内に別のチェックが必要です)、それはほとんど間違いなく常に間違っているケースです。そして、それが正しいところに事件が起こったとしても、それはコードを書くための正しい方法ではないほど慣用的に間違っている。そのコードを見た人は、すぐにためらって「それはバグだ」と言ってください。そして、おそらく著者を平手打ちします(著者があなたの上司である場合を除き、その場合は裁量が推奨されます)。
feof()
ループの制御に使用するのが悪い理由