这是错误的,因为(在没有读取错误的情况下),它比作者预期的循环多执行一次。如果存在读取错误,循环将永远不会终止。
考虑以下代码:
#include <stdio.h>
#include <stdlib.h>
FILE *Fopen(const char *path, const char *mode);
int
main(int argc, char **argv)
{
FILE *in = argc > 1 ? Fopen(argv[1], "r") : stdin;
unsigned count = 0;
while( !feof(in) ) {
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;
}
这个程序将持续打印输入流中字符数加一的结果(假设没有读取错误)。考虑输入流为空的情况:
$ ./a.out < /dev/null
Number of characters read: 1
在这种情况下,在读取任何数据之前调用了
feof()
,所以它返回false。进入循环,调用
fgetc()
(并返回
EOF
),并增加计数。然后调用
feof()
并返回true,导致循环中止。
这种情况在所有类似的情况下都会发生。只有在流上的读取遇到文件结束时,
feof()
才会返回true。
feof()
的目的不是检查下一次读取是否会达到文件末尾。
feof()
的目的是确定先前读取函数的状态,并区分错误条件和数据流的结束。如果
fread()
返回0,则必须使用
feof
/
ferror
来决定是否发生错误或是否消耗了所有数据。同样,如果
fgetc
返回
EOF
。
feof()
只有在fread返回零或
fgetc
返回
EOF
之后才有用。在此之前,
feof()
始终返回0。
在调用feof()
之前,始终需要检查读取的返回值(无论是fread()
、fscanf()
还是fgetc()
)。
更糟糕的是,考虑一种情况,即发生读取错误。在这种情况下,
fgetc()
返回
EOF
,
feof()
返回false,并且循环永远不会终止。在使用
while(!feof(p))
的所有情况下,必须在循环内部至少进行一次
ferror()
检查,或者至少将while条件替换为
while(!feof(p) && !ferror(p))
,否则可能会出现无限循环的情况,可能会产生各种垃圾数据作为无效数据进行处理。
总之,虽然我不能确定永远不会出现语义上正确编写"
while(!feof(f))
"的情况(尽管在发生读取错误时必须在循环内部进行另一个检查并使用break来避免无限循环),但几乎可以肯定它总是错误的。即使有一种情况下它是正确的,它也是如此习惯上错误,以至于不是编写代码的正确方式。任何看到那段代码的人都应该立即犹豫并说:“那是个错误”。并可能批评作者(除非作者是你的上司,这时请谨慎行事)。
编辑:正确编写代码的一种方法,演示了
feof
和
ferror
的正确用法:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
FILE *in = stdin;
unsigned count = 0;
while( getc(in) != EOF ){
count++;
}
if( feof(in) ){
printf("Number of characters read: %u\n", count);
} else if( ferror(in) ){
perror("stdin");
} else {
assert(0);
}
return EXIT_SUCCESS;
}
feof()
控制循环是不好的?使用feof()
函数来控制循环可能会导致错误。feof()
函数在文件结尾返回 true,因此当文件被读取完毕时,它将返回 true。但是,在循环中使用feof()
不能保证在读取到文件结尾之前结束循环,因为当文件结尾被读取时,下一次迭代将开始并尝试读取数据,导致读取错误和未定义行为。作为替代方案,最好使用文件 I/O 函数的返回值来控制循环。例如,在读取文件时,可以使用fgets()
函数,如果返回 NULL,则表示已经读取到文件结尾。这种方法可以确保循环结束的正确性,并且避免了潜在的错误。 - Grijesh Chauhan