官术网_书友最值得收藏!

  • 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比較無效。但是,程序員不能指望靠編譯器來提醒,畢竟警告消息可以被忽略,而且并不是所有編譯器都具備這樣的功能。

主站蜘蛛池模板: 泰顺县| 云安县| 镇康县| 左权县| 昆明市| 烟台市| 南安市| 略阳县| 格尔木市| 台山市| 嘉祥县| 馆陶县| 米易县| 萝北县| 安阳市| 沾化县| 三穗县| 扶绥县| 象山县| 饶河县| 磐石市| 南康市| 邵东县| 北安市| 嘉祥县| 涡阳县| 新建县| 揭西县| 许昌县| 宁国市| 库尔勒市| 富顺县| 赤城县| 五华县| 德江县| 临沧市| 定襄县| 得荣县| 北流市| 舒兰市| 嘉鱼县|