在C語言中,有個符號大家都應該很熟悉,那就是EOF(End of File),即文件結束符。但是很多時候對這個理解並不是很清楚,導致在寫代碼的時候經常出錯,特別是在判斷文件是否到達文件末尾時,常常出錯。
1.EOF是什麼?
在VC中查看EOF的定義可知:
#define EOF (-1)
EOF只是代表一個整形常量-1。因此很多人認為在文件的末尾存在這個結束標志EOF,這種觀點是錯誤的。事實上在文件的末尾是不存在這個標志的。那麼有人會問那下面的程序如何解釋?
char ch; while((ch=fgetc(fp))!=EOF) { printf("%c\n",ch); }
書上都通過這樣的代碼去判斷是否讀取到文本文件末尾,就是當讀取到EOF的時候就結束操作。這種理解是錯誤的。
先看一下函數fgetc的原型:
int fgetc(FILE *fp);
事實上在fgetc函數內部,每次都是讀取一個字節的數據,而且這一個字節的數據是以unsigned即無符號型處理的,然後將這一個字節的數據賦給一個int型變量作為返回值返回,因此無論從文件中讀取的是什麼數據,作為無符號型賦值給一個int型變量,返回值不可能是負數。比如當讀取到數據0xFA時,因為是以無符號處理的,因此在將0xFA賦值給int型變量的時,int型變量的高位是填充0的(為什麼填充0,跟匯編語言裡面的符號擴展類似,在後面會提到),因此返回的結果是0X00 00 00 FA,始終不會是負數.而若讀取到文件末尾的時候,即沒有數據可供讀取的時候,那麼返回EOF,即-1,這個-1是一個int型常量,二進制表示是0x FF FF FF FF。
上面的代碼具有很大的局限性,因為其只能判斷是否到達文本文件的末尾,而不能對二進制文件進行准確的判斷。因為正常情況下,文本文件中無論如何是無法讀取到-1(0x FF)這個數據,因此可以判斷。但是對於二進制文件不同,很可能讀到的一個字節的數據就是0xFF,那麼返回值此時就是-1,但是此時還未到達文件末尾,造成錯誤的判斷。
那麼有沒有辦法解決?可以將ch定義為int型即可。
下面來比較一下下面這段程序和上面程序在執行時的區別。
int ch; while((ch=fgetc(fp))!=EOF) { printf("%c\n",ch); }
假如在文件中讀取到的數據是0xFA。
那麼對於上面一段程序的執行過程是:
將0xFA先賦值給一個int型變量(假如是a),那麼此時a為0x 00 00 00 FA,當將返回值a返回給變量ch時,由於ch是char型的,只有8位,那麼只將a的低8位賦給ch,那麼此時ch為0x FA,而ch是作為有符號處理的,那麼此時ch的值肯定是負數。
而若將ch定義為int型,執行過程為:
將0xFA先賦值給一個int型變量(假如是a),那麼此時a為0x 00 00 00 FA,當將返回值a返回給變量ch時,由於ch也是int型的,因此ch為0x 00 00 00 FA,是一個正數,兩段程序執行得到的結果完全不同。
下面看一下若讀取到的數據是0x FF(此時未到文件末尾)時,是什麼結果。
若ch為char型,則當將返回值0x 00 00 00 FF返回時,取低8位賦給ch,那麼此時ch為-1,此時會誤判為到達文件末尾;
而若ch為int型,則當將返回值0x 00 00 00 FF返回時,ch的值為0x 00 00 00 FF,此時ch不為-1,不會誤判為文件末尾。
(當然上面所述成立必須是在讀取不出錯的情況下才成立)
所以很多情況下會用到函數feof. www.2cto.com
二.feof
feof函數的原型是
int feof(FILE *fp);
若到達文件末尾則返回一個非零值,否則返回0。
在VC中查看feof函數的定義:
#define _IOEOF 0x0010
#define feof(_stream) ((_stream)->_flag & _IOEOF)
可知feof函數判斷是否到達文件末尾時與_flag這個標志有關。
下面看一下這段程序:
#include<stdio.h> #include<stdlib.h> int main(void) { FILE *fp; int ch; if((fp=fopen("test.txt","w+"))==NULL) { printf("can not open file\n"); exit(0); } for(ch=65;ch<=70;ch++) { fputc(ch,fp); } rewind(fp); while(feof(fp)==0) { ch=fgetc(fp); printf("%0X\n",ch); } fclose(fp); return 0; }
執行結果是:
41
42
43
44
45
46
FFFFFFFF
Press any key to continue
為什麼最後打印結果會多打印一個FFFFFFFF?不是只往文件中寫入了數據65-70麼?
先看一下C++ Reference中關於feof函數的描述(C++ Reference是一個比較好的網站,裡面是關於C++所有庫函數的描述,網址在博客首頁的鏈接中有,http://www.cplusplus.com/reference/):
Checks whether the End-of-File indicator associated with stream is set, returning a value different from zero if it is.
This indicator is generally set by a previous operation on the stream that reached the End-of-File.
從描述中可知,只有當與文件關聯的流到達文件末尾時,此時若再進行讀取操作,文件結束的標志(上面所述的_flag)才會被重新置位。
因此在上述程序中,當讀取完最後一個字節的數據後,文件結束標志並沒有被置位,只有當位置指針到達末尾時,再發生讀取操作時,而此時又沒有數據可供讀取,因此返回-1,所以打印出的結果中會多一個FFFFFFFF,在這之後才會將_flag重新置位,此時feof函數才能檢測出已經到達了文件末尾。
那麼可以通過下面的辦法解決這個問題:
ch=fgetc(fp); while(feof(fp)==0) { printf("%0X\n",ch); ch=fgetc(fp); }
這樣就不會多打印一個FFFFFFFF了。
在上面提到匯編語言中符號擴展的問題,其實在C語言中屬於數據類型轉換的范疇。下面簡要說明一下:
符號擴展只針對將字長小的數據賦給字長大的數據時存在,若是字長大的數據賦給字長小的數據,取低位即可。
下面看一段程序:
#include<stdio.h> int main(void) { unsigned char ch1=0XFF; char ch2=0XFF; char ch3=0X73; int a=ch1; int b=ch2; int c=ch3; printf("%d\n%d\n%d\n",a,b,c); return 0; }
執行結果為:
255
-1
115
原因是由於ch1、ch2、ch3都是char型變量,只占一個字節,區別在於ch1是無符號的,在將ch1賦值給a時,ch1是看做無符號數據進行處理的,那麼在填充a的高位是用0去填充;而對於ch2和ch3都是有符號的,那麼在填充高位時就要注意了,若ch2的最高位為0,那麼表示ch2是正數,此時填充高位用0填充,而若ch2的最高位為1,則填充高位數據用1填充。
如程序執行的結果所示,由於ch2的最高位為1,那麼在填充b的高位的時候會用1去填充,那麼b為0X FF FF FF FF;而ch3的最高位為0,那麼填充c的高位用0填充,所以c的值為0x 00 00 00 73.
作者:海 子