| 形式 | 名稱 | 操作順序 | 表達式值 | 變量最終值 |
++i | 前綴自增 | 先增後用 | 增後的值 | i+1 |
--i | 前綴自減 | 先減後用 | 減後的值 | i-1 |
i++ | 後綴自增 | 先用後增 | 增前的值 | i+1 |
i-- | 後綴自減 | 先用後減 | 減前的值 | i-1 |
經典範例 3-16:逐行解析
- int i = 8;
- printf("%d\n", ++i); // 9 (i=9)
- printf("%d\n", --i); // 8 (i=8)
- printf("%d\n", i++); // 8 (i=9)
- printf("%d\n", i--); // 9 (i=8)
- printf("%d\n", -i++); // -8 (i=9) -(i++) → 先取 i=8 再 i=9
- printf("%d\n", -i--); // -9 (i=8) -(i--) → 先取 i=9 再 i=8
經典範例 3-17:多次自增陷阱
- int i = 5, j = 5, p, q;
- p = (i++) + (i++) + (i++); // 三個後綴:用舊值相加,再各自增
- q = (++j) + (++j) + (++j); // 三個前綴:各自增後用新值相加
- printf("%d,%d,%d,%d", p, q, i, j);
解析 p:
- 三個
i++ 表達式值分別為 5, 5, 5 → 相加 = 15
- 每個
i++ 後 i 自增 1,共增 3 次 → i 最終 = 8
解析 q:
- 三個
++j:第一個 j→6 值 6,第二個 j→7 值 7,第三個 j→8 值 8
- 相加 = 6+7+8 = 24
- j 最終 = 8
輸出:15,24,8,8
綜合優先級實戰範例
// 表達式: ++i - (j++) + (k--) (假設 i=5, j=5, k=5)
// 求值順序:
1. ++i → i=6, 值 6
2. j++ → 值 5, j=6
3. k-- → 值 5, k=4
4. 6 - 5 + 5 → 6
// 最終: i=6, j=6, k=4, 表達式值 6
⚠️ 核心陷阱與避坑鐵律
🚫 陷阱 1:同一表達式中多次修改同一變量 (未定義行為)
i = i++ + ++i; // ❌ 未定義行為!C 標準不保證求值順序
i = i++ + 1; // ❌ 同理
a[i] = i++; // ❌ 同理
鐵律:同一表達式中,一個變量最多被修改一次(修改包括賦值、自增、自減、函數副作用)。
🚫 陷阱 2:函數參數求值順序未規定
printf("%d %d %d", ++i, i++, --i); // ❌ 參數求值順序未定義,結果不可移植
🚫 陷阱 3:後綴自增優先級高於解引用
*p++ // 等價 *(p++):先取 *p,再 p 指向下一個
(*p)++ // 先取 *p,再將該值加 1
🚫 陷阱 4:整數除法截斷導致的邏輯錯誤
double avg = sum / n; // ❌ 若 sum/n 為整數除法,先截斷再轉 double
// 正確
double avg = (double)sum / n; // 或 sum / (double)n
最佳實踐清單
- 複雜表達式加括號:
(a + b) * c 比依賴優先級更清晰
- 自增自減單獨成行:
i++; ... 而非混在複雜表達式中
- 前綴優於後綴:不需要舊值時用
++i,避免臨時變量
- 整數除法警惕截斷:需浮點結果時強制轉換一個操作數
- 開啟編譯器警告:
-Wsequence-point -Wparentheses
- 避免單表達式多副作用:拆分為多語句,清晰可維護
總結
C 語言算術運算符體系的核心複雜度在於:15 級優先級、左右結合性混合、前後綴自增自減語義差異、整數除法截斷語義、以及序列點規則禁止的多重副作用。掌握這些機制,不僅能正確理解表達式求值,更能避開未定義行為這一 C 語言最大的雷區。現代最佳實踐:用括號表達意圖、自增自減獨立成行、複雜計算拆分中間變量,以換取代碼的可讀性、可移植性與安全性。
- 上一篇文章: C語言賦值運算符與賦值表達式:從基礎到復合賦值的完整指南
- 下一篇文章: C語言數據類型轉換:自動轉換規則與強制轉換完整指南
Copyright ©
程式碼之家 All Rights Reserved