經典面試題:int i = 3; i = i++; cout << i; 輸出 3 還是 4?正確答案:未定義行為。本文從 C++ 標準角度深入剖析序列點機制,揭示編譯器優化背後的設計考量。
程式執行中特定時間點,在此點之前所有副作用(變數修改、I/O 等)已完成,之後尚未開始。通俗說:前面的陳述句「執行乾淨了」,後面的「還沒開始」。
||、&&、?:、,(逗號運算子)第一個運算元之後i = i++ 是 UB?i = i++ 位於兩個序列點之間(完整表達式內)。標準規定:兩序列點間執行順序未指定。編譯器可選擇:
i++(i→4,傳回 3)→ 再賦值 i = 3 → 結果 3i、右值 i++ 的舊值 3 → i = 3 → 再執行 i++ → 結果 4兩種結果皆合標準,程式不可依賴特定順序。
++ 優先級雖高於 =,但「優先級」只決定運算元結合方式,不決定副作用發生時間。編譯器可先取值、延後副作用,不違反優先級規則。
為編譯器優化留空間。例:int j = i++;
temp=i; i+=1; j=temp;(需暫存器)j=i; i+=1;(省暫存、指令更少)Java 等高階語言強制指定順序,犧牲優化空間換取可預測性。
「相鄰兩序列點間,一個物件至多修改一次;若被修改,則僅為取得新值而讀取一次。」
違反例:
i = i++:修改兩次(賦值 + 遞增)a[i] = i++:同時讀取 i(陣列索引)與修改 i,非為取得新值f(i++, i++):單表達式修改 i 兩次若類別重載 operator||、operator&&,它們退化為普通函數呼叫,**不再擁有序列點語意**,參數求值順序同樣未定義。
序列點是 C/C++ 追求極致效能的代價。現代 C++ (C++17) 引入「定序關係」取代序列點概念,精確規定運算子順序(如賦值右運算元定序於左運算元之前),但理解經典模型仍對維護舊代碼、理解底層機制至關重要。