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

C語言進階筆記:短路求值、參數求值順序、自增副作用與未定義行為

     編輯:關於C

1. 邏輯運算符短路求值:安全編程的基石

C 語言邏輯與(&&)和邏輯或(||)具備短路求值特性:若左操作數已能決定整體結果,右操作數不再求值。

防禦性編程經典模式


// 避免除零:d==0 時短路,不執行 n/d
if (d != 0 && n / d > 0) { /* ... */ }

// 避免空指針解引用:p==NULL 時短路,不執行 *p
if (p == NULL || *p == '\0') { /* ... */ }

這兩個例子若無短路特性,將導致運行時崩潰。短路求值是 C 語言標準保證的行為,可放心用於防禦性檢查。

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

C 標準未規定函數實參的求值順序,編譯器可自由選擇(常見為右到左,但不可依賴)。

經典陷阱示例


int i = 3;
printf("%d, %d\n", i, i++); 
// 可能輸出:4, 3 (右到左求值)
// 也可能輸出:3, 3 (左到右求值)
// 甚至其他結果 —— 未定義行為!

結論:絕不在同一函數調用的多個實參中對同一變量進行修改或依賴修改後的值。

3. 前綴/後綴自增與副作用:序列點規則

++i(前綴)返回遞增後的值;i++(後綴)返回遞增前的值。但副作用(內存寫入)何時完成由序列點決定。

多重修改導致未定義行為


int i = 3;
int j = (++i) + (++i) + (++i); 
// 未定義行為!i 在兩個序列點之間被修改多次
// GCC 輸出:j=16, i=6 (但不可依賴)

混合前後綴同樣危險


int i = 1, j = 1, k;
i = (i++) + (++i); // UB: i 既作為左值賦值,又在右邊被修改兩次
k = (j++) + (++j); // UB
// GCC 輸出:i=5, j=3, k=4 (僅供參考)

標準規定:序列點與未定義行為

C99/C11 標準(6.5 段):

在前後兩個序列點之間,某個標量對象的值至多被修改一次;若被修改,則前一次值僅用於確定要存儲的新值。

違反此規則即產生未定義行為:程序可能崩潰、輸出錯誤結果、甚至看似正常運行但在不同編譯器/優化級別下表現不同。

安全編程準則

  1. 一條語句只做一件事:避免在複雜表達式中混合修改與取值同一變量。
  2. 拆分複雜表達式:用臨時變量保存中間結果。
    
    // 不安全
    a[i++] = b[i++];
    
    // 安全
    int idx = i++;
    a[idx] = b[i];
    
  3. 啟用編譯器警告:-Wall -Wextra -Wsequence-point (GCC/Clang) 可捕獲大多數序列點違規。
  4. 理解短路求值邊界:邏輯運算符(&&、||、?:、,)引入序列點,左操作數副作用確保在右操作數求值前完成。

總結表

代碼模式行為分類是否可移植建議
d != 0 && n/d短路求值 (標準保證)✅ 是推薦使用
f(i++, i++)參數求值順序未指定 + 多重修改❌ 否嚴禁
i = i++ + ++i無序列點多重修改❌ 否 (UB)嚴禁
i = (i++) + (++i)同上,括號不改變序列點❌ 否 (UB)嚴禁
a = b++ + c++修改不同對象,合法✅ 是允許

結語

C 語言給予開發者極大自由,也要求極高自律。理解序列點、未指定行為、未定義行為的區別,避開副作用陷阱,是從「能跑通」進階到「寫得對、跑得穩」的必經之路。

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