原文出處: http://blog.chinaunix.net/space.php?uid=53564&do=blog&id=2099623
讀ANSI C标準, 或K&R, 或C: A reference manual時, 往往會碰到對某個語言特性這樣的描述. 這三者到底是什麼意思, 我的粗略印象, 從implement-defined, 到unspecified, 到undefined, 依次越來越不靠譜, 越發危險, 越發不可移植. 僅有這樣的模糊認識是不夠的, 實際上, 求證于典籍之後, 還是不十厘清朗.
關于為什麼會有這3個晦澀的短語, 來迷惑我這樣的平民百姓, 難道是所謂語言律師們給我下的套?
Rationale中說:

我的了解是:
未指定的行為, 未定義行為, 和實作定義的行為用于歸類那些 本标準沒有, 或是不能完全描述其屬性的程式的結果. 對其分類的目的是為了允許 不同的編譯器實作可以有一定程度的靈活性. 這樣也鼓勵編譯器實作品質成為市場 競争的主導因素. 同時也能在允許編譯器在實作一些受歡迎的語言擴充的同時, 還 能打着遵循語言标準的金字招牌. 标準中告知性的附錄J把語言中的這些行為分門 别類地歸納到上述三種行為.
未指定的行為給具體實作者留下了一定的自由度. 這種自由不能越過編譯程式失敗 的極限(包括編譯程式失敗, 下面對"未定義行為"的讨論更證明了這一點), 因為無 論如何, 隻要任何實作不引起未定義行為, 所有可能的行為都算是"正确的".
未定義行為等于給實作者發了免死金牌, 實作者可以放任程式中存在錯誤, 隻因為 這些錯誤太難應對. 同時未定義行為也為可能的語言擴充打開了一扇門: 實作者可 以通過對标準中官方聲稱的未定義的行為作出定義來增強語言.
C99标準中說:
3.4.1 實作定義的 這個是三者之中最好了解的, 它有幾個特點: 1. 具體的編譯器自己做決定. 2. 但不能是拒絕編譯, 也就是說不能編譯失敗. 3. 編譯器必需在文檔中說明它選擇的行為. 4. 标準本身并不提供可選的方案(與 unspecified 相比)
3.4.3 未定義的行為
基本上, 未定義最能讓具體編譯器天馬行空, 為所欲為. 包括編譯失敗. 這可能是最省事的做法. 舉例說不可移植的特性, 或錯誤的程式結構(gcc的__attribute__((***)), 或是在表達式中允許塊結構, 算不算是), 或是錯誤的資料(這個想不出例子). 對出現這些情況, 而本标準又對具體實作未作要求的. 算作未定義行為.
舉例是整型溢出.
3.4.4 未指定的行為
标準中給定了2種或更多的選擇, 除此之外标準沒有額外地要求什麼情況該選擇哪種行為. 也就是說, 未指定的行為是讓編譯器實作者做選擇題. 但沒有要求其選擇必需是一緻的, 編譯器可以在同一個編譯單元中, 一會兒選擇這種做法, 一會選擇另外的做法.
久負盛名的C FAQ(中譯名稱是<<你必須知道的495個C語言問題>>)中關于這個問題, 也有相關的讨論
這裡把unspecified的翻譯為"不确定的", 我覺得有些不妥, 這裡的解釋說, unspecified, 跟未定義類似, 但無需提供文檔. 這加劇了我的一些迷惑, 原因是這個C FAQ應該也是相當的權威. "類似"是個模糊的詞, 不中心解惑這個問題. 另外, 至少unspecified 在一些明确的方面和未定義不同, 那就是, 實作者的行為上限是編譯失敗(包括編譯失敗), 雖然我想不出一個編譯器所能引起的比編譯失敗更嚴重的後果, 但正如它所說, 未定義行為比你想象的還要未定義, 或許它可以讓電腦當機, 引發太陽黑子.
該書中的最後陳述乍一看很有建設性. 那就是, 不管這三種行為誰是誰, 你全都要避免踩到雷區. 要求也不小, 你要能記住語言中所有的 implement-defined, unspecified, undefined的行為. 不容易.
但是, 看了The New C standard之後, 就會知道, 要避免所有的unspecified行為幾乎不可能, 比如b+c 表達中, b和c誰先被讀取就是未指定的. 該書中說: A blanket guideline recommendation prohibiting the use of any construct whose behavior is unspecified would be counterproductive.
光是 unspecified 的行為, 我統計了一下, 50條(統計完這個我發現<<The New C standard>>中就有這麼一句話, 是以我隻統計了這一個, undefined的個數是直接從該書引用了, 190個. )
從這50條分析來看, 并非所有的未指定行為, 都是在語言标準中給了可選方案, 讓實作者做選擇就可以了, 比如對靜态變量的初始化的方式和時機. 标準中就沒有給出任何建議的方案.
下面我把C99 标準中的這幾頁PDF 抽出來, 單存成一個檔案. 有興趣的可以自己看看.
|
|
關于這三個概念, 在C-A reference manual中怎麼解釋, 不幸的是, 我搜遍了這本書的PDF檔案, 找到的多處unspecified, 都不是解釋這個概念本身的. 也許這本書中沒有對此的解釋.
現在, 除了C-A reference manual一書, 我們還有The New C Standard(這本電子書可以在網上免費下載下傳.), 一本逐句解釋ANSI C标準, 長度超1600頁的大部頭經典.
讀了相關的解釋, 果然受用, 整個事态逐漸明朗了.
這本書的注釋澄清了下面的幾個問題: 我一開始以為的 implement-defined行為是三者之中最穩定可靠的, 這種想法是不對的, unspecified行為有多種原因, 一種原因是雖然某些行為的細節是unspecified的, 但整個程式的輸出對于所有可能的unspecified行為卻可能不受影響, 有些行為如果要明确指定, 則需要描述當時的上下文環境, 控制流的分析, 表達式的樹結構, 代碼優化算法, 是以雖然理論上可以對推測出象求值順序這樣問題, 但這麼做是很不現實的.
另一個澄清的問題是, unspecified與undefined的一個重要差別是, 前者是針對合法的程式, 後者則是針對非法的程式.
The New C standard, 名字有些誤導, 它不是在定義另一個新的C語言标準, 而是全景式地解讀既有的C99标準. 強烈推薦給拿C當回事的程式員放在手邊參考. 個人認為有了這本書, 普通人讀ANSI C标準不再是那麼痛苦的事了.