“牙里長嘴”和“a+=a-=a*a”


  題目典故出自相聲《反正話》:甲說一句話,乙則把詞顛倒過來說。例如甲說“桌子”,乙說“子桌”。當甲說出“我嘴里長牙”后,乙按約定的規則說了一句“我牙里長嘴”。這時“包袱”立刻抖響,滿堂捧腹。
  捧腹的原因是因為“牙里長嘴”荒誕得出乎人們的預料,人們無法想象“牙里”如何能夠“長嘴”。但是從另一方面講,必須承認“我牙里長嘴”在語法上是中規中矩的——它的荒誕不經並非是由於沖破了語言規范的藩籬,而是由於其內涵荒誕——它描述的是一種根本無法想象的、無意義的情形。
  相聲是語言的藝術,編程也是語言的藝術。巧合的是,用C語言也同樣會講出這種“牙里長嘴”的話,這就是所謂的“未定義行為”(Undefined behavior,后面簡稱UB)。最著名的UB恐怕就是譚浩強所寫的自稱“主流教材”的《C程序設計》中二十多年來一直津津樂道的“a+=a-=a*a”這個表達式了,這個表達式就是典型的“牙里長嘴”。為什么這么說呢?
  我們都知道,程序設計語言是一種人為定義的形式語言,因此這種語言所表達的含義也是人為定義的。譬如 1 + 1 這個表達式,它的含義就是要求計算機求出 1 + 1 的值。但是  1 + INT_MAX (注:INT_MAX是在limits.h中定義的一個宏,表示該編譯器上int數據類型所能表示的最大的值)的含義是什么呢?對不起,C語言壓根就沒有規定這樣寫的含義究竟是什么。這就是所謂的UB。 1 + INT_MAX 這種寫法的意義C語言沒有任何規定,換句話說,誰也不知道它的含義是什么。所以這就是“牙里長嘴”。
  有依據嗎?有。C標准中描述“+”運算時有這樣一句話:If  an  exceptional  condition   occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.
  標准總是這樣駢五駢六得讓人難以理解,原因很簡單,標准追求的是嚴謹,可讀性從來不是它所追求的首要目標。所以在這里得容我用俗話解釋一下。它的意思就是,在表達式求值時,如果發生了什么意外情況,比如1/0,這在數學上就沒有解釋,或者求值結果不在對應類型所能表示范圍內( 1 + INT_MAX就是這種情況,兩個int類型數據相加應該得到一個int類型的值,但現在這個值卻超出了int類型的表示范圍),那么這個表達式究竟是什么意思,C語言說它不知道。
  所以問題就是,你寫了1 + INT_MAX 這樣的表達式,連C語言都不知道是什么意思,你自己知道你到底說的是什么意思嗎?你可能以為你知道,就如同你以為你知道“牙里長嘴”是什么意思一樣,但那只能是一個錯覺,因為誰都不知道“牙里長嘴”應該是什么樣子。
  “主流教材”中二十多年來一直津津樂道的“a+=a-=a*a”同樣也是“牙里長嘴”,因為C語言規定:Between  the previous and  next sequence point an object shall  have its  stored value modified at most once by the evaluation of an expression.
  這句話的意思是說,在相鄰兩個序點(sequence point)之間,同一個數據對象的值最多可以通過表達式求值改變一次。更通俗一些解釋就是,在一個沒有&&、||、,?:運算的表達式中,同一個數據對象的值最多可以改變一次。這是程序員寫表達式必須遵守的一個基本規則。違反這條規則的代碼,就是“牙里長嘴”。因為其行為是C語言未定義的,因而這樣的代碼是錯誤的。
  但有些人不這樣看。有一種常見的看法是,UB不算是錯誤,無法通過編譯才算錯誤。這無異於說“牙嘴里長”才是錯誤,而“牙里長嘴”則不算錯誤。這種看法無疑是膚淺和荒唐的。一個程序員寫出了C語言並沒有規定其含義而他自己則居然以為他自己知道含義的代碼,可能正確嗎?
  堅持UB不是錯誤的人往往有一條似是而非的理由,那就是編譯器沒有報錯。但問題是C語言僅僅要求編譯器看到“牙嘴里長”這樣的話才必須報錯,而並沒有要求編譯器看到“牙里長嘴”這樣的話報錯。這就如同聽相聲聽到“牙里長嘴”時,大家只是哈哈一笑,絕對不會有人大聲糾正一樣。所以編譯器不報錯並不能證明代碼沒有錯,正如
   int i;
   scanf("%d",i);
這樣荒唐的寫法在很多編譯器上都不會報錯一樣。
  scanf("%d",i);這樣的代碼很多編譯器會給出警告,“a+=a-=a*a”在某些“聰明”的編譯器上也會得到警告,這就如同“牙里長嘴”不會得到糾正只會得到哄堂大笑一樣。
  所以繞來繞去,問題又回到了最初的起點,“a+=a-=a*a”這種“牙里長嘴”的代碼究竟有沒有意義?如果你認為“牙里長嘴”荒謬絕倫,那么“a+=a-=a*a”無疑就是錯誤的。如果你一本正經地認為“牙里長嘴”不荒謬,那么能否麻煩您在牙里長個嘴給大家瞧瞧?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM