(読み取りエラーがなければ) 作成者が予想するよりも 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
)、 count がインクリメントされます。その後、feof()
が呼び出されて true を返し、ループが中止されます。
これは、そのようなすべての場合に発生します。 ストリームの読み取りがファイルの終わりに到達するまでfeof()
true を返しません。の目的は、次の読み取りがファイルの終わりに到達するかどうかを確認することではありません。の目的は、以前の読み取り関数のステータスを判断し、エラー状態とデータ ストリームの終了を区別することです。0 を返す場合は、 /を使用して、エラーが発生したか、またはすべてのデータが消費されたかを判断する必要があります。同様に if を返します。 fread が 0 を返した後、または fread が を返した後にのみ有用です。それが起こる前は、常に 0 を返します。feof()
feof()
fread()
feof
ferror
fgetc
EOF
feof()
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))
(ただし、読み取りエラーで無限ループを回避するために、ループ内で別のチェックをブレーク付きで行う必要があります)。 )、ほぼ確実に間違っている場合です。そして、それが正しいケースが発生したとしても、それは慣用的に間違っているため、コードを書く正しい方法ではありません。そのコードを見た人は、すぐに躊躇して「これはバグだ」と言うべきです。そして、作者を平手打ちする可能性があります (ただし、作者があなたの上司である場合を除きます。その場合、裁量が推奨されます)。