本節書摘來自異步社群《c陷阱與缺陷》一書中的第1章,第1.3節,作者 【美】andrew koenig,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視
c語言的某些符号,例如/ 、 、和=,隻有一個字元長,稱為單字元符号。而c語言中的其他符号,例如/和 = = ,以及辨別符,包括了多個字元,稱為多字元符号。當c編譯器讀入一個字元'/'後又跟了一個字元'*',那麼編譯器就必須做出判斷:是将其作為兩個分别的符号對待,還是合起來作為一個符号對待。c語言對這個問題的解決方案可以歸納為一個很簡單的規則:每一個符号應該包含盡可能多的字元。也就是說,編譯器将程式分解成符号的方法是,從左到右一個字元一個字元地讀入,如果該字元可能組成一個符号,那麼再讀入下一個字元,判斷已經讀入的兩個字元組成的字元串是否可能是一個符号的組成部分;如果可能,繼續讀入下一個字元,重複上述判斷,直到讀入的字元組成的字元串已不再可能組成一個有意義的符号。這個處理政策有時被稱為“貪心法”,或者,更口語化一點,稱為“大嘴法”。kernighan與ritchie對這個方法的表述如下,“如果(編譯器的)輸入流截止至某個字元之前都已經被分解為一個個符号,那麼下一個符号将包括從該字元之後可能組成一個符号的最長字元串。”
需要注意的是,除了字元串與字元常量,符号的中間不能嵌有空白(空格符、制表符和換行符)。例如,= =是單個符号,而= = 則是兩個符号,下面的表達式
與表達式
的含義相同,而與
的含義不同。同樣地,如果/是為判斷下一個符号而讀入的第一個字元,而/之後緊接着,那麼無論上下文如何,這兩個字元都将被當作一個符号/,表示一段注釋的開始。
根據代碼中注釋的意思,下面的語句的本意似乎是用x除以p所指向的值,把所得的商再賦給y:
而實際上,/被編譯器了解為一段注釋的開始,編譯器将不斷地讀入字元,直到/出現為止。也就是說,該語句直接将x的值賦給y,根本不會顧及到後面出現的p。将上面的語句重寫如下:
或者更加清楚一點,寫作:
這樣得到的實際效果才是語句注釋所表示的原意。
諸如此類的準二義性(near-ambiguity)問題,在有的上下文環境中還有可能招緻麻煩。例如,老版本的c語言中允許使用=+來代表現在+=的含義。這種老版本的c編譯器會将
了解為下面的語句
亦即
是以,如果程式員的原意是
那麼所得結果将使其大吃一驚。
另一方面,盡管/*看上去像一段注釋的開始,在下例中這種老版本的編譯器會将
當作
這種老版本的編譯器還會将複合指派視為兩個符号,因而可以毫無疑問地處理
而一個嚴格的ansi c編譯器則會報錯。