scanf 的返回值 EOF 及 feof 函数
结论:EOF是在头文件stdio.h中预定义的一个宏,而eof(end of file)是一个与标准输入/输出流相关联的标志位。当文件指针已经指向文件尾且再次尝试读取时,eof标志会被设置。同时,某些函数会在读取到文件尾(即eof标志被设置)时返回EOF作为标识。
另注:
笔者经常会在C进行文件I/O操作时出现文件读取实际尚未结束(未读取至文件末尾),但系统显示已读取至文件尾的情况。具体原因如下。
在过去的操作系统中,文件以文件存储占用的扇区数作为文件大小的计数单位,故而如果文件的大小不能恰好填满存储文件的最后一个扇区,则需要对该扇区剩余的空间进行填充,过去填充使用的是值为26(0x1a)的字节,也就是某些系统中 Ctrl+Z 的对应码值,故而在读取文件过程中,读取到值26,则系统认为文件已经到达末尾,不再读取。在 windows 环境下,终端输入可以使用 Ctrl + z 作为输入的 EOF 标志,而 linux 环境下,则可使用 Ctrl + d 作为输入的结束标志。
现在的操作系统开始使用字节作为文件大小的计数单位,但古老的习惯还是被保留下来。为了避免文件读取过程中存在值为26的数据使得系统误认为文件结束而停止读取,可以将文件以二进制文件格式打开。
EOF
EOF常被在程序中用于判断(文件)缓冲区是否结束,实际在头文件 stdio.h 中定义。
可以看到EOF是一个头文件中预定义的宏,其值为 -1 。也就是说,在程序中使用的EOF实际为一个预定义的常数。而许多的函数会在读取至文件末尾或出现错误时,会返回EOF,作为处理状态的一种描述,如下文的getchar.
getchar(getchar C++ Reference)
函数声明:int getchar(void);
函数功能:当函数读取成功时,会返回成功读取的字符数据(转化为 int 类型),若失败,则返回值为EOF,也就是上文说的值为 -1 的宏定义。
可以从上面的介绍中看到,函数除返回EOF外,还会设置标准输入( stdin )的 eof 标志/error标志,可供feof或ferror函数使用。也就是说,EOF(宏定义)与eof标志并不是同一个东西。
getchar函数在头文件stdio.h中定义,具体实现如下:
显然,读取字符数据并指向下一个待读取的数据操作是由 *stdin->_ptr++ 来实现的,而返回 EOF 以及将上文提及的error标志和eof标志则是由函数_filbuf实现。
从上面资料可以得到结论:
(1)对一般的文件/输入流,存在一个指向待读取数据位置的内部的指针(如上面的stdin->_ptr),每次读取数据完成该内部指针会移动指向下一个待读取数据;
(2)文件/输入流还存在两个特殊的标志 eof 和 error ,它们可以分别被函数feof和ferror处理,当文件读取至文件末尾或出现错误时,相应的标志位eof/error会被设置;
(3)一些函数在发生错误或读取至文件末尾时,会返回EOF,其为值为 -1 的宏,同时设置文件/输入流的某些标志;
feof( feof C++ Reference)
函数声明:int feof(FILE *stream); //stream为对应的文件流的标志
函数功能:会检查与文件对应流的eof( end of file )标志是否被设置,如果被设置则返回非零值,如未被设置,则返回0.
注意:feof函数只检查eof标志是否被设置,其本身并不会设置eof标志,设置eof标志的是试图进行数据读取的操作。
示例:
1 #include<stdio.h> 2 3 int main(void) 4 { 5 FILE *ptr = fopen("a.txt","r"); 6 7 if(!ptr) 8 { 9 printf("打开文件失败!\n"); 10 return -1; 11 } 12 13 char ch; 14 int count = 0 ; 15 16 while(!feof(ptr)) //检测到EOF标志则停止 17 { 18 count++; 19 ch = fgetc(ptr); 20 printf("%c",ch); 21 } 22 printf("%5d",count); 23 printf("\n"); 24 25 fclose(ptr); 26 return 0; 27 }
示例代码读取的a.txt的内容为"12".代码会逐字节显示文件的内容(19、20行),以及读取的次数(18、22行)。
执行结果下图所示:
(1)EOF标志未被设置时,feof函数会返回0,eof标志被设置后,feof返回非零值;
(2)每次读取成功后,指向文件流内部的指针会顺序移动读取的字数个字节(对文本来说),这样使得内部指针总是指向待读取的下一个字符;
(3)执行流程 : feof返回值为0,第一次循环,ch = ‘1’,count = 1,流内部的位置指针指向‘2’;feof返回值为0,第二次循环,ch = ‘2’,count = 2,流内部的位置指针指向文件末尾,但此时EOF并未被设置;feof返回值仍为0,第三次循环,会试图访问文件的末尾,count = 3,eof 标志被设置,下一次的feof返回值为非0值,故而结束循环;(笔者在CodeBlocks12.13中调试时,第三次循环时ch的值为-1,即为EOF,printf并没有将其显示至控制台)
文件流内部存在标志读取位置的内部指针,该位置指针指向文件尾并不会设置对应文件流的eof 标志,只有当该位置指针指向文件尾,并再次试图进行顺序读操作时,才会设置eof标志。
EOF为一个定义的宏常量,而eof标志为与文件/输入流相关的一个标志,当位置指针已指向文件末尾并再次试图读取输入时,会设置eof标志,同时为了表明发生的情况,函数一般会返回EOF用来表示文件读取至末尾或发生错误。