【摘 要】C語言的內部運算符很豐富,其中最難理解的是在使用過程中最易得到模棱兩可結果的自增自減運算符,特別是遇到多個自增自減運算符連續出現在表達式中的時候,結果更是讓人無法估測。本文結合詞法分析中的“貪心法”對這兩個運算符的使用做了詳細說明。
【關鍵詞】貪心法 自增自減 詞法分析
【中圖分類號】G642 【文獻標識碼】A 【文章編號】1674-4810(2013)12-0080-02
C語言在計算機軟件開發中的作用日益重要,它以獨特的魅力征服了很多編程工作者,已成為世界上廣泛流行的、最有發展前途的計算機高級語言。它適用于編寫各種系統軟件,也適用于編寫各種應用軟件。針對C語言的教學,筆者談談對自增自減運算符的幾點看法。
一 自增和自減運算符的作用和特點
自增運算符(++)和自減運算符(――)都是單目運算符,它們的作用分別是使操作數加1和減1,換句話說:x=x+1;同++x,x=x-1;同――x,自增和自減運算符可用在操作數之前(前綴形式),也可放在操作數之后(后綴形式)。例如:“x=x+1;”可寫成“++x;”或“x++;”,“x=x-1;”可寫成“――x;”或“x――;”,但是表達式中這兩種用法是有區別的,首先自增運算符和自減運算符在操作數之前(前綴形式)“++x,――x”:先使變量x的值加1或減1,再使用變量x的值。其次自增運算符或自減運算符在操作數之后(后綴形式)“x++,x――”:先使用變量x的值,再使變量x的值加1或減1。
在教學中我們應該注意以下幾點:(1)使運算對象的值在原來的基礎上加1或減1是自增運算符和自減運算符的特點。所以自增或自減運算其實和賦值表達式的功能是一樣的。(2)運算符兩邊的運算對象的數據類型可以是整型的,也可以是實型的,但不能是常量或表達式。所以++5、(a+b)--等都是不合法的。(3)表達式若是由自增或自減運算符構成的,它的運算符就可以出現在運算對象的前面,也可以出現在運算對象后面,也就是前綴和后綴形式都可以,對于運算對象而言,結果是一樣的,但從表達式的角度看,結果就是不一樣的。(4)從優先級上來看,運算符“++”和“--”的結合方向是“從右到左”。假設有一表達式-a++,其中a的初始值為1,因為在運算符中,就相當于-(a++)運算,結果為-1,然后a自增為2。
二 自增和自減運算符的結合性
自增和自減運算符都是“從右到左”的結合方向,如果說a=3,那么計算:-a++,實際上就相當于計算-(a++)這個表達式,這時自增運算符“++”為后綴形式,(a++)的值為3,而-(a++)的值為-3,然后a的值自增為4。像這種簡單的自增自減運算,我們按運算符的優先級和結合性處理還是比較容易解決的。若是遇到多個自增自減運算符連續出現在表達式中的時候,結果就可能模棱兩可,有時更是讓人無法估測。下面讓我們來看一個復雜一些的例子:
例1:a+++++b的含義是什么?
我們為了驗證它的含義,編寫了一個小程序:
int main()
{
int a = 0;
int b = 0;
int c = 0;
c = a+++++b;
printf (\" a+++++b The result is %d\",c);
return 0;
}
左值就是可以在“=”左邊,能被賦予值的東西;右值則是在“=”右邊,可以賦值給別人的東西。所以左值必須是有內存空間的東西;而右值則既可以是變量,也可以是常量和某種表達式,只要能提供一個值即可。但這個程序是不能通過編譯的,也就是說,盡管我們可以正確地理解這個表達式,但在編譯過程中會產生錯誤。為什么a+++++b會編譯錯誤呢?
第一,編譯器在讀入此語言時,遇到連續多個+,如+++++,自動識別位((++)++)+,即會++比+更優先識別。
第二,++運算需要左值的。a+++++b等價于((a++)++)+b。a++沒問題,但問題就在于a++卻不能作為左值,即不能被賦予值,因為a++是先返回a的值進行運算,然后再對a的引用加1。然而a的值(value-a)是不能作為左值的。所示((a++)++)錯誤。++a為什么可以作為左值呢?原因在于++a是對a的引用加1,然后返回a的引用。a的引用當然可以作為左值,被賦值了。
C語言中的某些符號,例如:/、*和=,只有一個字符長,稱為單字符符號。而C語言的其他符號,例如:+=和==,以及標識符,包括了多個字符,稱為多字符符號。當C編譯器讀入一個字符/后又跟了一個字符*,那么編譯器就必須做出判斷:是將其作為兩個分別的符號對待,還是合起來作為一個符號對待。C語言對這個問題的解決方案可以歸納為一個很簡單的規則:每一個符號應該包含盡可能多的字符。也就是說,編譯器將程序分解成符號的方法是,從左到右一個字符一個字符地讀入,如果該字符可以組成一個符號,那么再讀入下一個字符,判斷已經讀入的兩個字符組成的字符串是否可能是一個符號的組成部分;如果可能,繼續讀入下一個字符。重復上面的判斷,直到讀入的字符組成的字符串已不再可能組成一個有意義的符號。這個處理策略就被稱為“貪心法”或者“大嘴法”。對這種方法更專業的描述是:“如果(編譯器的)輸入流截止某個字符之前都已經分解為一個個符號,那么下一個符號將包括從該字符之后可能組成一個符號的最長字符串。”
需要注意的是,除了字符串與字符常量外,符號的中間不能有空白,比如,空格符、制表符、換行符。
C語言編譯器就是用貪心算法對它進行分解的,按照貪心算法分解a+++++b為:((a++)++)+b,因為a++的結果為左值,而左值不能再進行運算,故a+++++b編譯不能通過,除非寫成a+++ ++b,在++b前面有個空格,這時候根據貪心算法分解為:(a++)+(++b),這么處理,編譯就可以通過了。
剛接觸C語言的人,往往會被C語言靈活的運算符搞得暈頭轉向,特別是處理自增自減運算的時候,甚至程序為何會得出結果都不理解,所以,多分析、多練習是關鍵。對于自增和自減運算符來說,主要掌握運算符的特點,如它的結合性、優先級,當然對C語言的編譯過程中的詞法分析也應有一定的了解,只要把握相關的規則,不管多復雜的問題也能解決。
〔責任編輯:龐遠燕〕