int* inc(int val) {
int a = val;
a++;
return &a; // 錯誤:a 在棧上,函數返回後內存無效
}
int main() {
int *val = inc(10);
printf("%d", *val); // UB:可能輸出 11、垃圾值、崩潰
}
正確做法:
void inc(int *p) { (*p)++; }int *p = malloc(sizeof(int)); *p = val+1; return p;(調用者負責 free)static int a; return &a;
int a=10, b=20, c=30;
printf("%d..%d..%d", a+b+c, (b=b*2), (c=c*2));
// 輸出不可預測!
// GCC 右到左:60..40..60
// Clang 左到右:60..20..30
// 其他編譯器可能不同
鐵律:參數列表中不寫副作用表達式。拆分語句:
b *= 2; c *= 2;
printf("%d..%d..%d", a+b+c, b, c); // 確定輸出 60..40..60
printf("%d,%d,%d,%d", ++i, --i, i++, i--); // UB:i 被修改 4 次,讀取多次
i = 1; i = i + 1; // OK:單一修改,賦值運算符有序列點
int i = 1, j; j = i + (i = 2); // UB:讀 i 與寫 i 無序列點隔離
int a = 0, b = 0;
if (a++ || b++) { } // a=1, b=0(|| 序列點:左側真則右側不求值)
if (a++ && b++) { } // a=2, b=1(&& 序列點:左側假則右側不求值)
&&、||、?:、,(逗號運算符)引入序列點,保證左運算元副作用完成後再求右運算元。
f(a, (t=2, t+3), c); // 參數 2 使用逗號運算符:先 t=2,整個表達式值為 5
// 等價於:
t = 2;
f(a, t+3, c);
區別:函數參數列表中的逗號是分隔符,無序列點,求值順序未定;括號內的逗號是運算符,有序列點,左到右求值。
-Wall -Wextra -Wsequence-point (GCC/Clang) 可捕獲大多數 UB。-fsanitize=undefined (UBSan) 運行時檢測。