未定義行為(undefined behavior, UB)
想像一個桌遊,它的規則書涵蓋了每一種合法走法會發生什麼事,但對某一種特定的非法走法卻什麼都沒寫——不是「你輸了」,不是「重擲一次」,就只是一片空白。如果有玩家走了那一步,規則書對裁判完全沒有指引,於是接下來真的什麼事都可能發生。那片空白就是 C 所稱的未定義行為。它是整個語言裡最重要、也最被誤解的概念。
以下是精確的意義,值得慢慢讀:未定義行為是 C 標準「不施加任何要求」的行為。整個定義就這樣。它不代表「結果是隨機的」。它不代表「結果取決於你的機器」(那是另一個類別,叫做實作定義行為)。它甚至不代表「程式會當掉」。它的意思是標準完全撒手不管——一支觸發未定義行為的程式,沒有任何已定義行為,不只那一行沒有,甚至整次執行都可以視為沒有。由此導出一個關鍵後果:因為標準說正確的程式永遠不會觸發未定義行為,所以最佳化器「被允許假設它永遠不會發生」。它接著就能把推理建立在這個假設上,產生出來的程式碼一旦假設被證明為假,會做出與你所寫毫不相干的事。
為什麼重要:未定義行為不是理論上的奇談,而是現實世界裡一大部分當機與安全漏洞的根本原因。經典的陷阱是:一支帶有未定義行為的程式可能看起來運作得完美無缺——直到換了新版編譯器、提高了最佳化等級、或輸入略有不同,使那個假設浮現,程式就以一種和原始錯誤毫無明顯關聯的方式爆掉。誠實的工程立場很簡單:未定義行為永遠是錯誤,即使它今天剛好給了你想要的答案。
int a[5]; int x = a[10]; /* 越界存取:未定義行為 */ int *p = 0; int y = *p; /* 解參考空指標:未定義行為 */ int z = INT_MAX + 1;/* 有號數溢位:未定義行為 */
三個經典觸發點。標準對這幾行的任何行為都不保證——甚至不保證會當掉。
最常被重複的誤解:UB 不代表「隨機」、「會當掉」或「視平台而定」。它代表標準不施加任何要求,所以編譯器「可以假設它永不發生」——這正是悄悄能跑的 UB 之所以危險的原因。