C 標準(ANSI C/ISO C)未對某些代碼構造規定具體語義,編譯器可任意處理:產生任意結果、崩潰、甚至刪除硬碟(理論上)。這不是缺陷,而是為極致效能留出的優化空間。
a += a++ — 側效應順序未定義int a = 3; a += a++; // 結果可能是 6,也可能是 7,視編譯器而定
原因:賦值運算符 += 的左右操作數求值順序、側效應(a++ 修改 a)生效時機,標準均未規定。現代編譯器多數產生 7,但不可依賴。
int GCD(int a, int b) {
while (a %= b ^= a ^= b ^= a); // 未定義:b 被多次修改且無序列點
return b;
}
風險:雖然 GCC/Clang 常能正確編譯,但標準不保證。嚴格寫法應用暫存變數或標準庫 std::gcd (C++17)。
char str[] = "hello"; str[10] = '\0'; // 編譯通過,運行時破壞鄰近記憶體
C 不做邊界檢查,編譯器假設程式員保證索引合法,從而省去檢查指令,提升速度。
int *p = malloc(4*sizeof(int)); free(p); *p = 0; // Use-after-free:未定義行為 int *q = (int*)0x12345678; *q = 42; // 任意位址寫入:未定義行為
釋放後記憶體可能被重新分配、映射保護頁、或單純殘留舊值。硬體可能觸發段錯誤,也可能默默破壞數據結構。
int x;
printf("%d", x); // 輸出隨機棧內容
C 不自動初始化區域變數(為效能)。GCC 早期版本甚至以此為由在未定義行為時啟動遊戲(如 nethack)作為警示。
char *s = "hello"; s[0] = 'H'; // 未定義:字串字面量通常在唯讀段
正確寫法:char s[] = "hello";(分配在棧上可修改)。
| 考量 | 說明 |
|---|---|
| 效能優先 | 不插入邊界檢查、初始化、側效應排序指令,生成極精簡機器碼 |
| 硬體抽象 | 不同 CPU 對溢位、移位、非對齊訪問行為不同,C 不強求統一 |
| 優化自由度 | 編譯器可假設 UB 不發生,激進優化(如刪除不可能的分支、向量化循環) |