- C陷阱與缺陷
- (美)安德魯·凱尼格
- 1169字
- 2021-01-15 16:46:06
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比較無效。但是,程序員不能指望靠編譯器來提醒,畢竟警告消息可以被忽略,而且并不是所有編譯器都具備這樣的功能。
- Android應用程序開發與典型案例
- Practical DevOps
- C語言程序設計實踐教程
- 機械工程師Python編程:入門、實戰與進階
- FLL+WRO樂高機器人競賽教程:機械、巡線與PID
- 軟件測試實用教程
- HTML5 APP開發從入門到精通(微課精編版)
- C/C++數據結構與算法速學速用大辭典
- Python預測之美:數據分析與算法實戰(雙色)
- 從零開始學算法:基于Python
- INSTANT Lift Web Applications How-to
- Appcelerator Titanium Smartphone App Development Cookbook
- Programming MapReduce with Scalding
- Data Visualization:Representing Information on Modern Web
- Mastering PyCharm