考慮如下代碼片段:
int x = 4, y; y = (x++) + (x++); // 表達式 1 y = (++x) + (++x); // 表達式 2
在 Windows (MSVC) 和 Linux (GCC) 上運行,得到**完全不同**的結果:
| 表達式 | MSVC (Windows) | GCC (Linux) |
|---|---|---|
(x++) + (x++) | 8 (4+4) | 9 (4+5) |
(++x) + (++x) | 12 (6+6) | 12 (6+6) |
(++x) + (++x) + (++x) | 21 (7+7+7) | 19 (6+6+7) |
C 標準(ISO/IEC 9899)明確規定:**在同一序列點內,對同一標量對象進行多次修改,或修改後又讀取其值(除非為了確定新值),行為未定義。**
(x++) + (x++):兩次後置遞增修改 x,且值被用於加法,無序列點隔離 → UB。(++x) + (++x):兩次前置遞增修改 x,同理 → UB。編譯器**不保證**任何特定結果,甚至同一編譯器不同優化等級、不同版本可能產生不同代碼。
x++:取值用於表達式,隨後立即遞增內存中的 x。兩次相加均用原值 4 → 4+4=8。++x:先遞增內存 x,再取值。第一次遞增至 5,第二次至 6,兩次相加用 6+6=12。x++:生成臨時變量 tmp = x; x = x + 1;。第一次 tmp=4,第二次 tmp=5(因第一次已遞增),相加 4+5=9。++x:對於兩個操作數,GCC 可能將 (++x)+(++x) 優化為 tmp = ++x + ++x;,導致第一次遞增至 5,第二次至 6,相加 5+6=11?實測為 12,說明 GCC 對兩個前置遞增有特殊處理(可能合併為 x+=2; tmp=x+x;)。三個時產生中間變量導致 6+6+7=19。// ❌ 錯誤示範
a[i++] = i; // 修改 i 又用 i 索引
x = x++ + ++x; // 多次修改 x
func(x++, x++); // 參數求值順序未定義,且多次修改 x
printf("%d %d", i++, i++); // 參數求值順序未定義
// ✅ 正確示範 int t1 = x++; int t2 = x++; y = t1 + t2; // 或 x++; int t1 = x; x++; int t2 = x; y = t1 + t2;
-Wall -Wextra -Wsequence-point)可捕獲大部分此類問題。參考:C11 標準 §6.5 Expressions, §5.1.2.3 Program execution。