EOF宏,C语言EOF宏详解

EOF 是 End Of File 的缩写,在 C 语言标准库中的定义如下:

#define  EOF  (-1)

迄今为止,关于 EOF 作用的观点各异。大多数程序员认为“文件中有一个 EOF 字符,用于表示文件的结尾”。但实际上,这个观点并不正确(或者说并不完整),在文件所包含的数据中,并没有什么文件结束符。从 EOF 宏的定义中可以看出,EOF 宏的值为 -1,属于 int 类型的数据,在 32 位系统中,可以表示为 0xFFFFFFFF。由此可见,EOF 并不是一个字符,也不是文件中实际存在的内容。那么,为什么会有这样的观点存在呢?

其实原因很简单,因为对一些数据读取函数(如 fgetc 与 getc 函数)而言,如果读到文件末尾(也可以理解为“如果不能从文件中读取”,即文件已经读完或者文件读取出错),则返回一个整数(-1),这就是所谓的 EOF。因此,EOF 宏不但能够表示读文件到了结尾这一状态(这种状态可以用 feof() 来检测),还能表示 I/O 操作中的读、写错误(通常可以用 ferror() 来检测)以及其他一些关联操作的错误状态。

看下面这段示例代码:
int main(void)
{
    FILE *fp=NULL;
    int c;
    fp=fopen("myfile.txt","r");
    if(fp == NULL)
    {
        printf("不能够访问该文件.\n");
        exit(1);
    }
    while((c=fgetc(fp)) != EOF)
    {
        printf("%x\n", c);
    }
    fclose(fp);
    fp=NULL;
}
对于 fgetc(或者 getc)函数,它返回一个 int 类型的数据。在正常情况下,fgetc(或者 getc)函数以 unsigned char 的方式读取文件流,并扩张为一个整数返回。换言之,fgetc(或 getc)函数从文件流中读取一个字节,并加上 24 个 0,成为一个小于 256 的整数,然后返回。

对于上面的示例代码,在正常读取的情况下,fgetc 函数返回的整数均小于 256(即 0x0~0xFF)。因此,就算读到了字符 0xFF,由于变量 c 被定义为 int 型,实际上这里的 c 等于 0x000000FF,而不是等于 EOF(即 0xFFFFFFFF),当然也不会误判为文件结尾。也就是说,即使是上面的示例代码遇到字符 0xFF,while 循环也不会结束,因为 0xFF 会被转化 0x000000FF,而不是 0xFFFFFFFF(EOF)。

既然如此,如果这里把 c 定义为 char 类型,那么其结果又将会怎样呢?如下面的示例代码所示:
char c;
fp=fopen("myfile.txt","r");
if(fp == NULL)
{
    printf("不能够访问该文件.\n");
    exit(1);
}
while((c=fgetc(fp)) != EOF)
{
    printf("%x\n", c);
}
因为文本文件中存储的是 ASCII 码,而 ASCII 码中 FF 代表空值(blank),所以如果读文件返回了 0xFF,也就说明已经到了文本文件的结尾处。也就是说,在语句“while((c=fgetc(fp))!=EOF)”中,当读取的字符为 0xFF 时,子语句“c=fgetc(fp)”中的“fgetc(fp)”的值由 0x000000FF 转换为 char 类型(即 c 等于 0xFF);而在执行子语句“c!=EOF”时,字符与整数比较,c 被转换为 0xFFFFFFFF,条件成立,遇到空格字符时就退出。由此可见,如果是二进制文件,其中可能会包含许多 0xFF,因此不能把读到 EOF 作为文件结束的条件,而此时只能使用 feof() 函数。

再假如,这里又将 c 定义为 unsigned char 类型,结果会与上面的 char 类型相同吗?如下面的示例代码所示:
unsigned char c;
fp=fopen("myfile.txt","r");
if(fp == NULL)
{
    printf("不能够访问该文件.\n");
    exit(1);
}
while((c=fgetc(fp))!= EOF)
{
    printf("%x\n", c);
}
在上面的“while((c=fgetc(fp))!=EOF)”语句中,就算是语句“fgetc(fp)”返回的结果为 -1(即 0xFFFFFFFF),但通过语句“c=fgetc(fp)”对其强制转换 unsigned char 类型,即 c 等于 0xFF。而在执行子语句“c!=EOF”时,c 被转换成 0x000000FF,永远也不可能等于 0xFFFFFFFF,因此表达式“c!=EOF”将永远成立。

由此可见,只有将 c 定义成 int 类型的变量,才能够与 fgetc 函数返回类型一致。