关注嵌入式安卓物联网行业及人才培养,每日更新,欢迎订阅及留言讨论~~~
作者:倪键树,嵌入式安卓物联网讲师。
fgetc与EOF的错综复杂关系
在正常的情况下,fgetc 以 unsigned char 的方式读取文件流,扩展为一个整数,并返回。换言之,fgetc 从文件流中取一个字节,并加上24个零,成为一个小于256的整数,然后返回。
int c;
while ((c = fgetc(rfp))!= -1) // -1就是 EOF
fputc (c,wfp);
解析:上面 fputc 中的 c 虽然是整数,但在 fputc将其写入文件流之前,又把整数的高24位去掉了,因此 fgetc,fputc 配合能够实现文件复制。到目前为止,把 c 定义为char仍然是可行的,但下面我们将看到,把 c 定义为 int 是为了能正确判段文件是否结束的,而char则不能判断文件是否结束。
2、判断文件结束
多数人认为文件中有一个EOF,用于表示文件的结尾。但这个观点实际上是错误的,在文件所包含的数据中,并没有什么文件结束符。对fgetc 而言,如果不能从文件中读取,则返回一个整数 -1,这就是所谓的EOF。返回 EOF 无非是出现了两种情况,一是文件已经读完; 二是文件读取出错,反正是读不下去了。
注意:在正常读取的情况下,返回的整数均小于256,即0x0~0xFF。而读不出返回的是(-1)0xFFFFFFFF。但是假如你用fputc把 0xFFFFFFFF 往文件里头写,高24位被屏蔽,写入的将是 0xFF。
3、将c定义 int读取0xFF 会使我们误判为结束吗?
不会,前提是接收返回值的 c 要按原型定义为 int。
如果下一个读取的字符将为 0xFF,则
int c;
c = fgetc (rfp);//本来读取为0xFF,自动扩展为c = 0x000000FF;
if (c != -1) // 当然不相等, -1 是 0xFFFFFFFF
fputc (wfp); //所以即使读取了OXFF 复制也成功!
字符0xFF,其本身并不是EOF。
4、将c定义 char,若读取为0xFF,复制将失败。
假定下一个读取的字符为 0xFF 则:
char c;
c = fgetc (rfp);// fgetc(rfp)的值扩展为 0x000000FF,强转char型变为c = 0xFF
if (c != -1) // 字符0xFF与整数-1比较,c 被扩展为0xFFFFFFFF(即-1),所以,条件成立,文件复制提前退出。
C被扩展为0xFFFFFFFF,由下面代码可以验证:
#include <stdio.h>
int main()
{
char c;
c = -1;
printf("%x",c);
return 0;
}
得到的结果为ffffffff。
while((c=fgetc(rfp))!=EOF) 中的判别条件成立,文件复制结束! 意外中止,复制失败!
5、将c定义为 unsigned char;
当读到文件末尾,返回 EOF也就是-1时,
unsigned char c;
c = fgetc (rfp);// fgetc (rfp)的值为EOF,即-1,即0xFFFFFFFF,降格为字节, c=0xFF
if ( c!= -1) // c 被扩展为 0x000000FF(即255),永远不会等于 0xFFFFFFFF
所以这次虽然能正确复制 0xFF,但却不能判断文件结束。事实上,在 c为unsigned char时,c != -1 是永远成立的,一个高质量的编译器,比如 gcc会在编译时指出这一点。
注:“关于char、signed char 和 unsigned char 的区别在哪里?”请看后边解析!
6、为何需要feof?
FILE *fp;
fp 指向一个很复杂的数据结构, feof 是通过这个结构中的标志来判断文件是否结束的。如果文件用 fgetc 读取,刚好把最后一个字符读出时, fp 中的EOF标志不会打开,如果这时用feof判断,将会得到文件尚未结束的结论,fgetc取得最后一个错误的-1。
当fgetc 返回 -1 时,我们仍无法确信文件已经结束,因为可能是读取错误! 这时我们需要 feof 和 ferror。
总结:EOF并不是存在于文件中的,而是一种状态,当读到文件末尾或者读取出错时就会返回这个值。(即使读取错误可能也被认为文件结束,所以就需要用feof 和 ferror来判断是不是真的文件结束了)
7、getchar(c)的使用
当用getchar(c)时,即使c定义成字符型,也可以结束,主要是c与-1比较时,c会从char转换为整型值,同c定义为int。
char、signedchar 和unsigned char 的区别在哪里???
ANSI C 提供了3种字符类型,分别是char、signed char、unsigned char。char相当于signed char或者unsigned char,但是这取决于编译器!!!这三种字符类型都是按照1个字节存储的,可以保存256个不同的值:
signedchar取值范围是 -128 到 127
unsigned char 取值范围是 0 到 255
但是char究竟相当于signed char呢还是相当于unsigned char呢??对于int类型的有: int==signed int,但是char不能简单以为char==signed char。要确定char究竟等同什么要基于不同的编译器做测试。
大多数机器使用补码来存储整数,在这些机器中按照整数类型存储的-1的所有位均是1,假设我的机器也是如此存储,就能据此判断char究竟是等于signed char还是unsigned char:
程序如下:
据此可以判断在我的机器上char==signed char
但是绝对不能武断认为char==signed char就像int==signed int一样天经地义,大部分人还是单纯认为char==signed char的,其实换位思考一下就清楚了,c规定了九种不同的整形其中有char和signed char 和 unsihned char,但是只有int 没有 signed int(二者相等),如果简单认为char就是等同于signed char的话那8种不就完事了???
如果编译器支持unsigned char和char的相等同,设置这个选项就会发现char==unsigned char。下面是对这种相等的设置。
-funsigned-char
-fno-signed-char
这两个编译时设定的参数是对char类型进行设置,决定将char类型设置成-unsigned char
-fsigned-char
-fno-unsigned-char
这两个编译时设定的参数是对char类型进行设置,决定将char类型设置成signed char。
如此 char就等同于unsigned char了
总结:小小的char类型,竟然有如此的玄机,你看明白了吗?!要时刻谨记fgetc的用法,和EOF的理解,写出严谨的程序。