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

C語言序列點與副作用深度解析:為什麼 i++ + i++ 是未定義行為

     編輯:C 語言入門

C 語言中「副作用」指修改對象或文件狀態的操作(賦值、自增、函數調用等),而「序列點」是執行時間線上的特殊節點:**此前所有副作用已完成,之後副作用未開始**。標準規定:兩個序列點之間,同一對象至多被修改一次。違反即導致未定義行為 (UB),表達式結果不可預測。

核心規則速覽

  • 語句結束 `;` 是序列點。
  • 完整表達式 (非子表達式) 計算結束是序列點。
  • 邏輯運算 `&&`、`||`、條件運算 `?:`、逗號運算符 `,` 引入額外序列點,保證左側先行。
  • 函數調用前 (實參求值後) 是序列點。

經典陷阱案例

案例 1:y = x++ + x++

int x = 1, y;
y = x++ + x++;  // UB!x 在兩個序列點(語句首/尾)之間被修改兩次

編譯器可能產生 2、3、4 等任意結果,甚至發射導彈 (理論上)。**切勿在單一表達式中多次修改同一對象**。

案例 2:printf("%d\n", i++ * i++); // i=7

輸出 **49** 而非 56。原因:後綴 `++` 承諾「序列點前完成修改」,但**不保證何時修改**。編譯器常選「先用舊值相乘,序列點前再雙增」。

案例 3:a[i] = i++

左側 `a[i]` 讀取 `i`,右側修改 `i`,**讀取未用於計算寫入值** → 違反「前值僅用於決定新值」規則 → UB。甚至 `i` 是否增 1 都不保證!

標準原文與中文對照

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

翻譯:在相鄰序列點間,對象值至多被修改一次;前值僅能用於確定要存儲的新值。

為什麼 &&、||、?:、, 有序列點?

保證**短路求值**語義:左側決定結果時右側不計算,需明確左右順序。逗號運算符保證左運算副作用完成後再右運算。

避坑清單

  1. 同一表達式中**勿多次修改**同一變量 (`i++ + i++`、`a[i]=i++`)。
  2. 函數實參求值順序**未規定**,勿寫 `func(i++, i++)`。
  3. 利用序列點拆分複雜表達式:`i++; y = i + i;` 明確順序。
  4. 開啟編譯器警告:`-Wsequence-point` (GCC/Clang) 可捕獲大多數 UB。

理解序列點與副作用,是寫出**可移植、無 UB** C 代碼的基石。下次見到花式自增表達式,直接重構 —— 別拿未定義行為賭明天的發布!

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