ANSI C標準定義了一些術語,用於描述某種編譯器的特點。對這些術語有一個比較好的瞭解,對於提供程式碼的可移植性和錯誤的排查都有幫助的。
- 不可移植的程式碼(unportable code)
由編譯器定義的(implementation-defined): 由編譯器設計者決定採取何種行動,也就是說在不同的編譯器中所採取的行為可能並不相同,但是它們都是正確的。每個編譯器的文件會給出具體的行為方式。
例如: 當整數向右移位時,要不要擴充套件符號位。
未指定的(unspecified): 在某些正確情況下的做法,標準並未規定應該怎樣做。
例如:引數求值的順序。
- 壞程式碼(bad code)
未定義的(undefined): 在某些不正確情況下的做法,標準並未規定應該怎樣做。編譯器可以什麼也不做,也可以發出一條警告資訊,或者中止程式。
例如:當一個有符號整數溢位時該採取什麼行動。
約束條件(a constraint): 這是一個必須遵守的限制或要求。如果你不遵守,那麼你的程式的行為就會變成像上面所說的屬於未定義的。分辨某種東西是否是一個約束條件是容易的,因為標準的每個主題都附有一個“約束(constraint)“小節,列出了所有的約束條件。
例如:%操作符的運算元必須屬於整形。所以在非整形數上使用%操作符肯定會引發一條錯誤資訊。
標準規定編譯器只有在違反語法規則和約束條件的情況下,才能產生錯誤資訊!這意味著所有不屬於約束條件的語義規則你都可以不遵循,而且由於這種行為屬於未定義的行為,編譯器可以採取任何行動,甚至不必通知你。
不屬於約束條件的例子:所有在C語言標準標頭檔案中宣告的識別符號均保留,所以不能宣告一個叫malloc()的函式,因為在標準標頭檔案中已經有一個這樣的函式。但是由於這個規定不是約束條件,因此可以違反它,而且編譯器甚至可以不警告你。
- 可移植的程式碼
嚴格遵循標準的(strictly-conforming): 一個嚴格遵循標準的程式應該是:
-
只使用已確定的特性
-
不突破任何由編譯器實現的限制
-
不產生任何依賴由編譯器定義的或未指定的或未定義的特性的輸出
這樣規定的主要目的就是最大限度地保證可移植性。這樣,不論你在什麼平臺上執行嚴格遵循標準的程式都會產生相同的輸出。
遵循標準的(strictly-conforming): 一個遵循標準的程式可以依賴一些某種編譯器特有的不可移植的特性。所以,一個程式有可能在一個特定的編譯器裡是遵循標準的,但在另一個編譯器裡卻是不遵循標準的。
C語言標準規範中的Annex J (informative) Portability issues
章節對哪裡行為屬於未指定的,哪裡行為屬於未定義的等有詳細的總結,可以參考。
參考文獻
- ISOIEC 9899-201x-C語言標準規範
- 《c專家程式設計》