首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 序列點深度解析:為什麼 i = i++ 是未定義行為?

     編輯:關於C++

經典面試題:int i = 3; i = i++; cout << i; 輸出 3 還是 4?正確答案:未定義行為。本文從 C++ 標準角度深入剖析序列點機制,揭示編譯器優化背後的設計考量。

什麼是序列點?

程式執行中特定時間點,在此點之前所有副作用(變數修改、I/O 等)已完成,之後尚未開始。通俗說:前面的陳述句「執行乾淨了」,後面的「還沒開始」。

標準規定的序列點位置 (C++03)

  • 完整表達式結束處(分號、控制結構條件判斷後)
  • 函數呼叫進入與返回時
  • ||、&&、?:、,(逗號運算子)第一個運算元之後
  • 初始化宣告結束處

為什麼 i = i++ 是 UB?

i = i++ 位於兩個序列點之間(完整表達式內)。標準規定:兩序列點間執行順序未指定。編譯器可選擇:

  1. 先算 i++(i→4,傳回 3)→ 再賦值 i = 3 → 結果 3
  2. 先算賦值左值 i、右值 i++ 的舊值 3 → i = 3 → 再執行 i++ → 結果 4

兩種結果皆合標準,程式不可依賴特定順序。

優先級誤區澄清

++ 優先級雖高於 =,但「優先級」只決定運算元結合方式,不決定副作用發生時間。編譯器可先取值、延後副作用,不違反優先級規則。

為什麼標準允許這種模糊性?

為編譯器優化留空間。例:int j = i++;

  • 嚴格序列:temp=i; i+=1; j=temp;(需暫存器)
  • 允許重排:j=i; i+=1;(省暫存、指令更少)

Java 等高階語言強制指定順序,犧牲優化空間換取可預測性。

避免 UB 的程式設計鐵律

「相鄰兩序列點間,一個物件至多修改一次;若被修改,則僅為取得新值而讀取一次。」

違反例:

  • i = i++:修改兩次(賦值 + 遞增)
  • a[i] = i++:同時讀取 i(陣列索引)與修改 i,非為取得新值
  • f(i++, i++):單表達式修改 i 兩次

重載運算子的特例

若類別重載 operator||、operator&&,它們退化為普通函數呼叫,**不再擁有序列點語意**,參數求值順序同樣未定義。

總結

序列點是 C/C++ 追求極致效能的代價。現代 C++ (C++17) 引入「定序關係」取代序列點概念,精確規定運算子順序(如賦值右運算元定序於左運算元之前),但理解經典模型仍對維護舊代碼、理解底層機制至關重要。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved