----節錄內容----
詞法分析中的「貪心法」C 語言的某些符號,例如/ 、* 、和=,只有一個字元長,稱為單字元符號。而 C 語言中的其他符號,例如/* 和 = = ,以及識別子,包括了多個字元,稱為多字元符號。當 C 編譯器讀入一個字元'/' 後又跟了一個字元'*',那麼編譯器就必須做出判斷:是將其作為兩個分別的符號,還是合起來作為一個符號對待。C 語言對這個問題的解決方案可以歸納為一個很簡單的規則:每一個符號應該包含盡可能多的字元。也就是說,編譯器將程式分解成符號的方法是,從左到右一個字元一個字元地讀入,如果該字元可能組成一個符號,那麼再讀入下一個字元,判斷已經讀入的兩個字元組成的字串是否可能是一個符號的組成部分;如果可能,繼續讀入下一個字元,重覆上述判斷,直到讀入的字元組成的字串已不再可能組成一個有意義的符號。這個處理策略有時被稱為「貪心法」,或者,更口語化一點,稱為「大嘴法」。Kernighan 與Ritchie 對這個方法的描述如下,「如果(編譯器的)輸入流截止至某個字元之前,都已經被分解為一個個符號,那麼下一個符號將包括從該字元之後可能組成一個符號的最長字串。」
需要注意的是,除了字串與字元常數,符號的中間不能嵌有空白(空白字元、Tab 和換行字元)。例如,== 是單一符號,而= = 則是兩個符號,下面的表達式
a---b
與表達式
a -- - b
的涵義相同,而與
a - -- b
的涵義不同。同樣地,如果/是為判斷下一個符號而讀入的第一個字元,而/之後緊接著*,那麼無論上下文如何,這兩個字元都將被當作一個符號/*,表示一段註解的開始。
根據程式碼中註解的意思,下面的語句的本意似乎是用x除以p所指向的值,把所得的商再賦給y:
y = x/*p /* p指向除數 */;
而實際上,/*被編譯器理解為一段註解的開始,編譯器將不斷地讀入字元,直到*/出現為止。也就是說,該語句直接將x的值賦給y,根本不會顧及到後面出現的p。將上面的語句重寫如下:
y = x / *p /* p指向除數 */;
或者更加清楚一點,寫作:
y = x/(*p) /* p指向除數 */;
這樣得到的實際效果才是語句註解所表示的原意。
諸如此類的準二義性(near-ambiguity)問題,在有的上下文環境中還有可能招致麻煩。例如,老版本的 C 語言中允許使用 =+來 代表現在 += 的涵義。這種老版本的 C 編譯器會將
a=-1;
理解為下面的語句
a =- 1;
亦即
a = a - 1;
因此,如果程式設計師的原意是
a = -1;
那麼所得結果將使其大吃一驚。
另一方面,儘管/* 看上去像一段註解的開始,在下例中這種老版本的編譯器會將
a=/*b;
當作
a =/ *b ;
這種老版本的編譯器還會將複合賦值視為兩個符號,因此可以毫無疑問地處理
a >> = 1;
而一個嚴格的 ANSI C 編譯器則會報錯。