[C/C++]诡异的fread,feof?

#include <stdio.h> 

int main(int argc, char** argv)
{
char buf[8193];
FILE* fp = fopen("tmp.bin", "wb+");
fwrite(buf, 1, 8192, fp);

int size_read = 0;
fseek(fp, 0, 0);
size_read = fread(buf, 1, 8193, fp);

/* here, all are ok, feof(fp) returns !0
*/
printf("8193, feof(fp)=%d\n", feof(fp));

fseek(fp, 0, 0);
size_read = fread(buf, 1, 8192, fp);

/* should feof(fp) return 0 or not? a true implementation should return 0
but now it is eof
*/
printf("8192, feof(fp)=%d\n", feof(fp));
fclose(fp);
return 0;
}

今天在网上看到有人讲fread和feof函数的诡异,代码如上。原文作者的意思是向文件中写入了8192个字符,然后读取8193个字符,程序提示文件读到尾了,这很正确;但是如果读取了8191个字符,按理说程序应该告诉文件到尾了,但是却没有。所以原文作者想不通了。然后他给了一个解决办法,原文链接在http://febird.iteye.com/blog/419790。
其实造成这种理解偏差的是feof的实现策略。MSVC版本的feof的实现如下:
int __cdecl feof (
FILE *stream
)
{
_VALIDATE_RETURN((stream != NULL),EINVAL, 0);
return( ((stream)->_flag & _IOEOF) );
}
可以看到,feof函数判断文件是否结束在于判断file stream的_flag的值是否含有_IOEOF。关键点就在于什么时候_flag的值修改为包含_IOEOF。想来想去,也只有在fread的过程中了。查看fread的MSVC版本的源代码,修改_flag值得部分有两处,分别为:
1:
stream->_cnt = _read(_fileno(stream), stream->_base, stream->_bufsiz);

#ifndef _UNICODE
if ((stream->_cnt == 0) || (stream->_cnt == -1)) {
#else /* _UNICODE */
if ((stream->_cnt == 0) || (stream->_cnt == 1) || stream->_cnt == -1) {
#endif /* _UNICODE */
stream->_flag |= stream->_cnt ? _IOERR : _IOEOF;
stream->_cnt = 0;
return(_TEOF);
}
2:
nread = _read(_fileno(stream), data, nbytes);
if (nread == 0) {
/* end of file -- out of here */
stream->_flag |= _IOEOF;
return (total - count) / elementSize;
}

可见_IOEOF值是否被_flag包含,在于_read函数的返回值是否是0。_read (不带缓冲) 函数是ReadFile函数的封装,fread (带缓冲) 函数调用的_read函数。ReadFile函数通过返回值表示Read动作是否执行成功,而没有直接的方式表示已经读取到了文件的结尾。所以feof函数实现的策略就变成了:如果本次的读取动作成功了,而且返回读取的字符数是0,那么就认为到达了文件的结尾。但是这里有一个疑问:如果本次读取动作成功了,但是读取到的字符比希望读取的字符少,那么是否也应该认为已经到了文件的结尾了呢?我认为是的,但是微软没有采用这种策略,不知道是为什么。

这样开始的诡异现象就可以解释了:写入8192字符的时候,文件会自动的增加一个末尾标记;当读取8193字符的时候,ReadFile函数会返回只读取了8192个字符,所以还需要再读一次,而第二次读取的时候由于读取到0字符,所以会被标记为 _IOEOF;当读取8192字符的时候,ReadFile函数会返回8192个字符,所以不需要再向后读,所以就不会出现读取0字符的结果,自然就不会被标记为 _IOEOF。

你可能感兴趣的:(Stream,File,null,include,FP,微软)