C 表達式中操作符優先級、結合性、副作用(++/--、賦值)交織極易產生未定義行為或邏輯錯誤。本文通過三組精心設計的測試用例,涵蓋位運算、關係/條件運算符、邏輯短路與副作用順序,幫你建立精確的心智模型。
#define PRINT(expr) printf(#expr " = %d\n", expr) int x=3, y=2, z=1; // 0b11, 0b10, 0b01 PRINT(x | y & z); // 3 | (2 & 1) = 3 | 0 = 3 PRINT(x | y & ~z); // 3 | (2 & ~1) = 3 | (2 & 0xFE) = 3 | 2 = 3 PRINT(x ^ y & ~z); // 3 ^ (2 & ~1) = 3 ^ 2 = 1 PRINT(x & y && z); // (3 & 2) && 1 = 2 && 1 = 1 x=1; y=-1; PRINT(!x | x); // (!1) | 1 = 0 | 1 = 1 PRINT(~x | x); // ~1 | 1 = -2 | 1 = -1 (全 1) PRINT(x ^ x); // 0 x <<= 3; PRINT(x); // 8 y <<= 3; PRINT(y); // -8 y >>= 3; PRINT(y); // -1 (算術右移保持符號位)
關鍵點:& 優先於 |、^;位運算符優先於邏輯運算符;~ 為按位取反;負數右移為實現定義(通常算術右移)。
int x=1, y=1, z=1; x += y += z; // x += (y += 1) → x=3, y=2, z=1 PRINT(x < y ? y : x); // 3<2?2:3 → 3 PRINT(x < y ? x++ : y++); // 3<2? x++ : y++ → y++=2, y=3 PRINT(x); PRINT(y); // 3, 3 PRINT(z += x < y ? x++ : y++); // z += (3<3? x++ : y++) → z+=3=4, y=4 PRINT(y); PRINT(z); // 4, 4 x=3; y=z=4; PRINT((z >= y >= x) ? 1 : 0); // (4>=4)→1, 1>=3→0 → 0 【關鍵:關係運算符左結合,結果為 0/1 再參與下一比較】 PRINT(z >= y && y >= x); // (4>=4)&&(4>=3) → 1
核心教訓:a >= b >= c **不等價於數學鏈式比較**,實為 (a>=b) >= c,中間結果為 0/1,極易寫錯。
#define PRINT3(x,y,z) printf(#x "=%d " #y "=%d " #z "=%d\n",x,y,z) x=y=z=1; PRINT3(++x || ++y && ++z); // ++x=2→真,短路,y,z 不變 → 2,1,1 x=y=z=1; PRINT3(++x && ++y || ++z); // ++x=2, ++y=2→真,短路 → 2,2,1 x=y=z=1; PRINT3(++x && ++y && ++z); // 全真 → 2,2,2 x=y=z=-1; PRINT3(++x && ++y || ++z); // ++x=0→假,++y=0→假,++z=0→真 → 0,-1,0 x=y=z=-1; PRINT3(++x || ++y && ++z); // ++x=0→假,++y=0→假,++z=0→真 → 0,0,-1 x=y=z=-1; PRINT3(++x && ++y && ++z); // ++x=0→假,短路 → 0,-1,-1
鐵律:&&、||、?:、, 為**序列點**,左運算元**完全求值含副作用**後再決定是否求右運算元。**絕不**在無序列點表達式中多次修改同一對象(如 i = i++ + ++i → UB)。
-Wsequence-point -Wunsequenced(GCC/Clang)捕獲潛在 UB。?: 中僅放置無副作用純計算,副作用移至語句層面。(a>=b) && (b>=c) 明確邏輯。