有些錯誤非得自己犯過了纔會記得住,非得自己理解了分析了才記得住。比如說用getchar(),fgetc()之類的函數來接收字符時,需要一個變量暫存,而且還要判斷是否讀到EOF,這個在 C陷阱與缺陷 裏面看到過,現在用到又忽略了。我們習慣於用char ch;while( ( c = getchar() ) != EOF ){} 來接收數據。這樣是存在問題的,雖然getchar等是接收字符,但是它的返回值卻是int型,爲什麼呢?因爲EOF在stdio中定義爲-1,不同於任何一個字符。負數在計算機中的存儲形式爲2的補碼,那麼-1 就是 0xFFFFFFFF了。char型肯定無法容納所有可能的字符(字符包括字母,數字,符號,特別是還有漢字!!),先看看字符的可能長度:
1.ASCII編碼中,1個英文字符存儲需要1個字節,
2.GB2312或GBK編碼中,一個漢字字符存儲需要2個字節,
3.UTF-8編碼中,一個英文字符存儲需要1個字節,一個漢字字符需要3到4個字節。
4.UTF-16編碼中,英文和漢字都需要2個字節。
5.UTF-32編碼中,任何字符都需要4個字節。
這下大家應該清楚了吧,不要以爲字符都是一個字節的。
特別是無法容納下EOF,來討論下char ch會帶來什麼隱患:
1.假設你輸入了一個2個字節的字符吧,0x3CFF,我也不知道代表什麼,那麼getchar返回0x3CFF,截取低字節給ch,ch=0xFF,然後ch要與EOF比較,ch會擴展爲32位。
如果編譯器認爲ch是有符號型的,ch就被擴展爲0XFFFFFFFF,這下可好,誤認爲是EOF了,while循環跳出,提前停止輸入。
2.假設真的碰到了EOF吧,截取爲FF給ch,如果編譯器認爲ch是無符號型的,ch就被擴展爲0x000000FF,這樣永遠也見不到EOF了!陷入死循環。
3.那爲什麼我偏偏使用了char ch,但是沒出問題呢?完全是巧合,許多編譯器對上述表達式的實現並不正確,的確對getchar的返回值做了截斷處理,但是它們在比較表達式中並不是比較c與EOF,而是比較getchar的函數返回值與EOF,編譯器如果這樣,那麼就能正常“運行”了。
正確的聲明:
#include <stdio.h>
int main()
{
int c; /* 改成:int c 就正確了 */
while( ( c = getchar() ) != EOF )
{
putchar( c );
}
return 0;
}