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

C 語言核心筆記:短路求值、參數求值順序與自增副作用陷阱

     編輯:關於C語言

1. 邏輯運算符短路求值

C 語言 `&&` 和 `||` 具有短路特性:若左側運算元已能決定結果,右側不會被求值。這不僅是優化,更是安全編程的基石。

經典防護模式


// 除零保護:d != 0 為假時,右側不執行,避免除零崩潰
if (d != 0 && n / d > 0) { /* ... */ }

// 空指針解引用保護:p == NULL 為真時,右側不執行,避免段錯誤
if (p == NULL || *p == '\0') { /* ... */ }

2. 函數參數求值順序:未指定行為

C 標準未規定函數參數求值順序。大多數實現(x86 cdecl)採用從右向左壓棧,但不可依賴。

經典陷阱


int i = 3;
printf("%d, %d\n", i, i++);  // 輸出可能 4,3(右向左)或 3,3(左向右)或其他

規則:同一表達式中對同一對象「讀取」與「修改」若無序列點隔離,行為未定義。上例中 `i` 既被讀取又被修改,且無序列點,屬 UB。

3. 前綴/後綴自增的副作用與序列點

關鍵概念:序列點。在序列點之前,所有副作用必須完成;在序列點之後,尚未開始。

表達式中的序列點

  • 完整表達式結尾(`;` 結尾)
  • `&&`、`||`、`?:`、` , `(逗號運算符)
  • 函數調用進入前(參數求值後)、返回後

GCC 行為演示(僅供理解,不可依賴)


// 多重副作用:未定義行為
i = 3;
j = (++i) + (++i) + (++i);  // GCC: j=16, i=6
// 解析:編譯器可能展開為 i=4; t1=4; i=5; t2=5; i=6; t3=6; j=t1+t2+t3;

int i = 1, j = 1, k;
i = (i++) + (++i);  // GCC: i=5
k = (j++) + (++j);  // GCC: k=4, j=3

為什麼結果不同?

編譯器對中間結果的暫存方式不同。標準不保證任何特定結果。

4. 實戰守則

場景正確寫法錯誤寫法
循環計數`for (int i = 0; i < n; i++)``a[i++] = i++;`
交換變量`tmp=a; a=b; b=tmp;``a ^= b ^= a ^= b;`
鏈表遍歷`while (p) { q = p->next; ... p = q; }``while (p = p->next)` 修改 p 同時讀取

5. 總結

  • 短路求值是語言保證的特性,放心用於防護
  • 參數求值順序未指定,永不在參數列表中修改同一變量
  • 單表達式中對同一變量多次修改/讀取修改 → 未定義行為
  • 寫出「聰明」的一行式代碼是反模式,清晰分步才是專業
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved