EOF宏,C語言EOF宏詳解

2020-07-16 10:04:26
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("%xn", 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("%xn", 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("%xn", 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 函數返回型別一致。