首先聲明:這些內容主要是面向C語言的初學者,尤其是正在學習C語言的學生。
在《C語言字符型數據(一)》中,我們對文本文件中的內容進行了簡單的加密處理。程序如下所示:
#include "stdio.h"
int main()
{
char ch;
freopen("original.txt","r",stdin) ; //輸入輸出被分別重定向到兩個文件。
freopen("result.txt","w",stdout);
ch=getchar();
while(ch!=EOF) //EOF指文件的末尾
{
if(ch>='A'&&ch<='Z'||ch>='a'&&ch<='z')
{
ch=ch+3;
if(ch>'z'||(ch>'Z'&&ch<='Z'+3))
ch= ch-26;
}
printf("%c",ch);
ch = getchar();
}
return 0;
}
有一個學生在練習上述程序時,誤將while(ch!=EOF) 寫成了while(ch=!EOF),程序編譯通過,運行後,result.txt文件中什麼結果都沒有。花了很長時間才最終發現了這個錯誤。其實很多初學者都曾犯過類似的錯誤。比如,在寫if語句的條件時,也很容易將if(ch!=EOF)寫成if(ch=!EOF),或者本來是判斷是否相等if(ch==’a’),結果寫成了if(ch=’a’)。對初學者而言,最大的困難是這類錯誤並沒有語法問題,從而很難查找,這也是C語言靈活性的一個體現。下面我們先分析錯誤的原因,再給出解決方法。
錯誤分析:
while後面的括號中可以是任何表達式,表達式首先被求值,然後判斷該值的真假,C語言判斷真假的規則是:零是假,非零的值都爲真。當寫成ch=!EOF時,這實際上是一個賦值表達式,因爲感嘆號現在是邏輯運算符“非”,它的優先級高於賦值“=”,所以ch=!EOF等價於ch=(!EOF)。EOF是一個常量,等於-1(非零爲真),所以!EOF的值爲假,注意邏輯表達式求值後的真假是用1和0來表示的,因此!EOF的值爲0,ch也被賦值爲0,表達式ch=!EOF的值等於ch的值,也爲0。因此,while後面的條件恆爲假,循環體一次都沒有執行。這就是爲什麼result.txt文件中什麼結果都沒有。
解決方法:
1.有牛人曾經給出過一個解決方案:在寫while或者if後面的條件時,把常量寫在左面,變量寫在右邊。這樣寫的好處是,當寫錯運算符時,編譯會報錯。比如上面的例子,建議寫成while(EOF!= ch),這樣寫並不影響判斷,但如果誤寫爲while(EOF=!ch),編譯時會報錯,因爲EOF是一個常量,常量是不能被賦值的。這樣就很容易糾正錯誤。
2.適當加入斷言來防止錯誤的發生。比如上面的程序中,如果while循環正常結束的話,ch應該等於EOF,因此我們可以斷言循環結束後ch等於EOF。加了斷言的程序如下所示:
#include "stdio.h"
#include"assert.h"
int main()
{
char ch;
freopen("original.txt","r",stdin);
freopen("result.txt","w",stdout);
ch=getchar();
while(ch!=EOF) //當這個表達式寫成while(ch=!EOF),運行時後面的assert會檢測到問題。
{
if(ch>='A'&&ch<='Z'||ch>='a'&&ch<='z')
{
ch=ch+3;
if(ch>'z'||(ch>'Z'&&ch<='Z'+3))
ch = ch-26;
}
printf("%c",ch);
ch = getchar();
}
assert(c==EOF); //正常情況下,循環結束時,c==EOF,斷言是正確的。當while後面的表達式寫錯時,循環
//一次都沒有執行,這時c!=EOF ,因此斷言會報錯。
return 0;
}
初學者可以自己上機去試試上面的程序,看斷言報錯的結果是怎樣的。