《C陷阱与缺陷》一第1章 词法“陷阱”1.1 =不同于==
本節書摘來自異步社區《C陷阱與缺陷》一書中的第1章,第1.1節,作者 【美】Andrew Koenig,更多章節內容可以訪問云棲社區“異步社區”公眾號查看
第1章 詞法“陷阱”
C陷阱與缺陷
當我們閱讀一個句子時,我們并不去考慮組成這個句子的單詞中單個字母的含義,而是把單詞作為一個整體來理解。確實,字母本身并沒有什么意義,我們總是將字母組成單詞,然后給單詞賦予一定的意義。
對于用C語言或其他語言編寫的程序,道理也是一樣的。程序中的單個字符孤立來看并沒有什么意義,只有結合上下文才有意義。因此,在p->s = "->";這個語句中,兩處出現的'-'字符的意義大相徑庭。更精確地說,上式中出現的兩個'-'字符分別是不同符號的組成部分:第一個'-'字符是符號->的組成部分,而第二個'-'字符是一個字符串的組成部分。此外,符號->的含義與組成該符號的字符'-'或字符'>'的含義也完全不同。
術語“符號”(token)指的是程序的一個基本組成單元,其作用相當于一個句子中的單詞。從某種意義上說,一個單詞無論出現在哪個句子,它代表的意思都是一樣的,是一個表義的基本單元。與此類似,符號就是程序中的一個基本信息單元。而組成符號的字符序列就不同,同一組字符序列在某個上下文環境中屬于一個符號,而在另一個上下文環境中可能屬于完全不同的另一個符號。
譯注:
如上面的字符'-'和字符'>'組成的字符序列->,在不同的上下文環境中,一個代表->運算符,一個代表字符串"->"。
編譯器中負責將程序分解為一個一個符號的部分,一般稱為“詞法分析器”。
再看下面一個例子,語句:
if (x > big) big = x;這個語句的第一個符號是C語言的關鍵字if,緊接著下一個符號是左括號,再下一個符號是標識符x,再下一個是大于號,再下一個是標識符big,依次類推。在C語言中,符號之間的空白(包括空格符、制表符或換行符)將被忽略,因此上面的語句還可以寫成:
if ( x > big ) big = x ;本章將探討符號和組成符號的字符間的關系,以及有關符號含義的一些常見誤解。
1.1 =不同于==
由Algol派生而來的大多數程序設計語言,例如Pascal和Ada,使用符號:=作為賦值運算符,符號=作為比較運算符。而C語言使用的是另一種表示法,符號=作為賦值運算,符號= =作為比較。一般而言,賦值運算相對于比較運算出現得更頻繁,因此字符數較少的符號=就被賦予了更常用的含義——賦值操作。此外,在C語言中賦值符號被作為一種操作符對待,因而重復進行賦值操作(如a=b=c)可以很容易地書寫,并且賦值操作還可以被嵌入到更大的表達式中。
這種使用上的便利性可能導致一個潛在的問題:當程序員本意是作比較運算時,卻可能無意中誤寫成了賦值運算。比如下例,該語句本意似乎是要檢查x是否等于y:
if (x = y) break;而實際上是將y的值賦給了x,然后檢查該值是否為零。再看下面一個例子,本例中循環語句的本意是跳過文件中的空格符、制表符和換行號:
while (c = ' ' || c == '\t' || c == '\n') c = getc (f);由于程序員在比較字符' '和變量c時,誤將比較運算符= =寫成了賦值運算符=。因為賦值運算符=的優先級要低于邏輯運算符 || ,因此實際上是將以下表達式的值賦給了c:
' ' || c == '\t' || c == '\n'因為 ' ' 不等于零(' ' 的ASCII碼值為32),那么無論變量c此前為何值,上述表達式求值的結果都是1,因此循環將一直進行下去直到整個文件結束。文件結束之后循環是否還會進行下去,這取決于getc庫函數的具體實現,在文件指針到達文件結尾之后是否還允許繼續讀取字符。如果允許繼續讀取字符,那么循環將一直進行,從而成為一個死循環。
某些C編譯器在發現形如e1 = e2的表達式出現在循環語句的條件判斷部分時,會給出警告消息以提醒程序員。當確實需要對變量進行賦值并檢查該變量的新值是否為0時,為了避免來自該類編譯器的警告,我們不應該簡單關閉警告選項,而應該顯式地進行比較。也就是說,下例
if (x = y) foo(); 應該寫作:if ((x = y) != 0) foo();這種寫法也使得代碼的意圖一目了然。至于為什么要用括號把x = y括起來,本書的2.2節將討論這個問題。
前面一直談的是把比較運算誤寫成賦值運算的情形,另一方面,如果把賦值運算誤寫成比較運算,同樣會造成混淆:
if ((filedesc == open(argv[i], 0)) < 0)error();在本例中,如果函數open執行成功,將返回0或者正數;而如果函數open執行失敗,將返回-1。上面這段代碼的本意是將函數open的返回值存儲在變量filedesc之中,然后通過比較變量filedesc是否小于0來檢查函數open是否執行成功。但是,此處的= =本應是=。而按照上面代碼中的寫法,實際進行的操作是比較函數open的返回值與變量filedesc,然后檢查比較的結果是否小于0。因為比較運算符= =的結果只可能是0或1,永遠不可能小于0,所以函數error()將沒有機會被調用。如果代碼被執行,似乎一切正常,除了變量filedesc的值不再是函數open的返回值(事實上,甚至完全與函數open無關)。某些編譯器在遇到這種情況時,會警告與0比較無效。但是,作為程序員不能指望靠編譯器來提醒,畢竟警告消息可以被忽略,而且并不是所有編譯器都具備這樣的功能。
總結
以上是生活随笔為你收集整理的《C陷阱与缺陷》一第1章 词法“陷阱”1.1 =不同于==的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 偏最小二乘法
- 下一篇: php 卡迪尔的秘密