op= 不只是語法糖
i += 1; // i 的值只計算一次
i = i + 1; // i 的值計算兩次(左值一次,右值一次)
實際差異示例:
int a[10];
int foo() { puts("foo called"); return 0; }
a[foo()] += 1; // foo() 呼叫 1 次
a[foo()] = a[foo()] + 1; // foo() 呼叫 2 次!
E1 op= E2 等價於 E1 = E1 op (E2),但 E1 只求值一次。這對有副作用的左值(如函數調用、++i、a[i++])至關重要。
expr1, expr2expr1,丟棄其值,再求 expr2,整個表達式值為 expr2 的值。expr1 的所有副作用在 expr2 求值前完成。
int a = (1, 2, 3); // a = 3
int b = 1, 2, 3; // 錯誤:宣告語法,非表達式
f(a, (t=2, t+3), c); // 參數 2 使用逗號運算符,t=2 先執行,值為 5
sizeof 與 typedefsizeofsize_t(無符號整數,<stddef.h>),不同平台寬度不同(32 位為 unsigned int,64 位為 unsigned long)。sizeof expression —— 不求值表達式(除 VLA)sizeof(type) —— 必須加括號sizeof(i++) 不會修改 i。typedeftypedef unsigned long size_t; → size_t 等價於 unsigned long。#define 區別:typedef 受作用域限制,參與類型檢查;#define 純文本替換。= += -= *= /= %= &= ^= |= <<= >>=++ --; 結束的表達式)。||、&&、?:、,(逗號運算符)的左運算元求值後。printf、scanf)每個轉換規範處理後。在兩個序列點之間:
① 同一標量對象被修改超過一次 → UB。
② 同一標量對象被修改後又被讀取,且讀取不用於確定該修改的值 → UB。
a = (++a) + (++a) + (++a) + (++a); // 修改 4 次,無序列點 → UB
i = i + 1; // OK:修改 1 次,讀取用於計算新值
a[i++] = i; // UB:i 被修改(++) 又被讀取(下標),無序列點
i = i++ + 1; // UB:修改(i++) 與賦值(=) 無序列點
i = i + 1; // 讀舊值 → 計算 → 寫新值,單一修改
*p++ = *q++; // p++、q++ 副作用在賦值序列點前完成,合法
a[i] = i + 1; // 讀 i 計算下標,讀 i 計算值,寫 a[i] —— 單一修改 a[i]
a && b:先求 a,若假則不求 b,結果為假;若真則求 b,結果為 b 的真值。a || b:先求 a,若真則不求 b,結果為真;若假則求 b,結果為 b 的真值。; 序列點隔離副作用。-Wsequence-point、-fsanitize=undefined 捕獲 UB。