運算符分類總覽
| 類別 | 運算符 | 關鍵點 |
| 後綴/原子 | () [] -> . ++ -- | 最高優先級,左結合 |
| 一元 | ++ -- + - ! ~ * & sizeof _Alignof | 右結合,sizeof 不計算表達式值 |
| 乘法性 | * / % | 左結合,% 僅整數 |
| 加法性 | + - | 左結合,指針算術特殊規則 |
| 移位 | << >> | 左結合,負數右移實現相關 |
| 關系 | < <= > >= | 左結合,結果 0/1 |
| 相等 | == != | 左結合,**別寫成 =** |
| 位與 | & | 左結合 |
| 位異或 | ^ | 左結合 |
| 位或 | | | 左結合 |
| 邏輯與 | && | 左結合,**短路求值** |
| 邏輯或 | || | 左結合,**短路求值** |
| 條件 | ?: | **唯一三目**,右結合 |
| 賦值 | = += -= *= /= %= <<= >>= &= ^= |= | 右結合,**返回左值** |
| 逗號 | , | 最低優先級,左結合,**序列點** |
核心陷阱與最佳實踐
1. = vs == (經典 Bug)
if (a = 5) // ❌ 賦值,結果 5 (真),永遠執行
if (a == 5) // ✅ 比較
// 防禦寫法:常量左側
if (5 == a) // 誤寫成 5 = a 會編譯錯誤
2. 短路求值副作用
int x = 0;
if (x || func()) // x 為 0,func() 會被調用
if (x && func()) // x 為 0,func() **不會**調用
// 依賴副作用的邏輯極易出錯,避免在 &&/|| 中放函數調用/賦值
3. 自增自減副作用與序列點
int i = 1;
int a = i++ + ++i; // ❌ 未定義行為:同一表達式多次修改 i 且無序列點
// 正確:拆分語句
int t1 = i++;
int t2 = ++i;
int a = t1 + t2;
4. 條件運算符結合性
a = b ? c : d ? e : f; // 解析為 a = b ? c : (d ? e : f) (右結合)
// 建議加括號明確意圖
a = b ? c : (d ? e : f);
5. 逗號運算符 vs 函數參數分隔
int a = (1, 2, 3); // a = 3,逗號運算符,返回最後表達式值
func(1, 2, 3); // 三個參數,非逗號運算符,無序列點保證求值順序
// C17 規定函數參數求值順序未指定,不要依賴順序
6. sizeof 不計算表達式
int i = 0;
size_t s = sizeof(i++); // s = sizeof(int),i **不會** 增加
// sizeof 類型/表達式均在編譯期確定大小,不產生代碼
優先級速查口訣(由高到低)
后缀 () [] -> .
一元 ++ -- + - ! ~ * & sizeof
乘除模 * / %
加减 + -
移位 << >>
关系 < <= > >=
相等 == !=
位与 &
位异或 ^
位或 |
逻辑与 &&
逻辑或 ||
条件 ?:
赋值 = += -= ...
逗号 ,
總結
- **優先級決定「誰先綁定誰」**,**結合性決定「同級誰先算」**。
- **短路求值 (&&/||/?:) 是語義保證,不是優先級**,利用它寫安全檢查:
p && p->next。
- **副作用 (賦值/自增/函數調用) 與複雜表達式混用 = UB 溫床**,拆分語句、用中間變量。
- **不確定時加括號**:寫
(a + b) * c 而非依賴記憶,代碼給人看,順便給編譯器看。