本節書摘來自異步社區《C專家編程》一書中的第1章,第1.6節,作者 【美】Perter Van Der Linde,更多章節內容可以訪問云棲社區“異步社區”公眾號查看
1.6 它很棒,但它符合標準嗎
不要添亂——立即解散ISO工作小組。
——匿名人士
ANSI C標準可以說是非常獨特的,我們可以從好幾個有趣的方面來說明這一點。它定義了下面一些術語,用于描述某種編譯器的特點。如果你對這些術語有一個比較好的了解,就有助于你理解什么東西能被語言接受,什么東西不能被語言接受。前兩個術語涉及不可移植的代碼(unportable code),接下來的兩個術語跟壞代碼(bad code)有關,而最后兩個術語則跟可移植的代碼(portable code)有關。
不可移植的代碼(unportable code):
由編譯器定義的(implementation-defined)——由編譯器設計者決定采取何種行動(就是說,在不同的編譯器中所采取的行為可能并不相同,但它們都是正確的),并作好文檔記錄。
例如:當整型數向右移位時,要不要擴展符號位。
未確定的(unspecified)——在某些正確情況下的做法,標準并未明確規定應該怎樣做。
例如:參數求值的順序。
壞代碼(bad code):
未定義的(undefined)——在某些不正確情況下的做法,但標準并未規定應該怎樣做。你可以采取任何行動,可以什么也不做,也可以發出一條警告信息,或者可以中止程序以及讓CPU陷入癱瘓,甚至可以發射核導彈(只要你安裝了能發射核彈的硬件系統)。
例如:當一個有符號整數溢出時該采取什么行動。
約束條件(a constraint)——這是一個必須遵守的限制或要求。如果你不遵守,那么你的程序的行為就會變成像上面所說的屬于未定義的。這就出現了一種很有意思的情況:分辨某種東西是否是一個約束條件是很容易的,因為標準的每個主題都附有一個“約束(constraint)”小節,列出了所有的約束條件。現在又出現了一個更為有趣的情況:標準規定[5]編譯器只有在違反語法規則和約束條件的情況下才能產生錯誤信息!這意味著所有不屬于約束條件的語義規則你都可以不遵循,而且由于這種行為屬于未定義行為,編譯器可以采取任何行動,甚至不必通知你!
例如:%操作符的操作數必須屬于整型。所以,在非整數數據上使用%操作符肯定會引發一條錯誤信息。
不屬于約束條件規則的例子:所有在C語言標準頭文件中聲明的標識符均保留,所以不能聲明一個叫作malloc()的函數,因為在標準頭文件里已經有一個函數以此為名。但由于這個規定不是約束條件,因此可以違反它,而且編譯器甚至可以不警告你!關于“interpositioning”這一小節的更多內容,參見第5章。

未定義的行為在IBM PC中引起CPU癱瘓!
未定義的軟件行為引起CPU癱瘓的說法并不像它乍聽上去那樣牽強。
IBM PC的顯示器以顯示控制芯片所提供的水平掃描速率工作。回掃變壓器(flyback transformer,一種產生高電壓的裝置,用于加速電子以點亮顯示器上的熒光物質)需要保持一個合理的頻率。
然而在軟件中,程序員有可能把視頻芯片的掃描速率設置成零,這樣就會產生一個恒定的電壓輸出到回歸變壓器的輸入端。這就使它起了電阻器的作用,只是把電能轉換成熱能,而不是傳送到屏幕。這會在數秒之內就把顯示器燒毀,那就是未定義的軟件行為會導致系統癱瘓的理由。
可移植的代碼(portable code):
嚴格遵循標準的(strictly-conforming)—— 一個嚴格遵循標準的程序應該是:
只使用已確定的特性。
不突破任何由編譯器實現的限制。
不產生任何依賴由編譯器定義的或未確定的或未定義的特性的輸出。
這樣規定的主要目的就是最大限度地保證可移植性。這樣,不論你在什么平臺上運行嚴格遵循標準的程序都會產生相同的輸出。事實上,在所有遵循標準的程序中,屬于這一類的程序并不多。例如,下面這個程序就不是嚴格遵循標準的:
#include <limits.h>
#include <stdio.h>
int main() { (void)printf("biggest int is %d", INT_MAX); return 0;}/*并不嚴格遵循標準:其輸出結果是由編譯器定義的。*/
在本書的剩余部分,我們通常并不強求例子程序嚴格遵循標準。因為如果這樣做會使文本看上去比較亂,而且不利于理解所討論的要點。程序的可移植性是非常重要的,所以在你的現實編碼中,應該始終要保證加上必要的類型轉換、返回值等。
遵循標準的(conforming)——一個遵循標準的程序可以依賴一些某種編譯器特有的不可移植的特性。所以,一個程序有可能在一個特定的編譯器里是遵循標準的,但在另一個編譯器里卻是不遵循標準的。它可以進行擴展,但這些擴展不能修改嚴格遵循標準的程序的行為。但是,這個規則并不是一個約束條件,所以對于你的程序中不遵循標準之處,你不要指望編譯器會給出一條警告信息指出你違反了規定!
上面所舉的幾個程序實例都是遵循標準的。