運算符優先級決定「誰先算誰後算」,結合性決定「同級誰先誰後」。本文提供**標準優先級表(15 級)**、**三大口訣速記法**、**經典陷阱案例**、**C vs C++ 差異**、**編譯器實現細節**,助你徹底征服優先級這座「必考大山」。
標準優先級表(C99/C11,高→低)
| 級 | 運算符 | 說明 | 結合性 | 目數 |
| 1 | `()` `[]` `->` `.` | 函數調用、下標、成員訪問 | 左→右 | 1/2 |
| 2 | `++` `--` `+` `-` `!` `~` `*` `&` `(type)` `sizeof` | 後置/前置自增減、一元正負、邏輯非、按位非、間接/取址、強制轉換、大小 | 右→左 | 1 |
| 3 | `*` `/` `%` | 乘、除、取模 | 左→右 | 2 |
| 4 | `+` `-` | 加、減 | 左→右 | 2 |
| 5 | `<<` `>>` | 左移、右移 | 左→右 | 2 |
| 6 | `<` `<=` `>` `>=` | 關系比較 | 左→右 | 2 |
| 7 | `==` `!=` | 等於、不等於 | 左→右 | 2 |
| 8 | `&` | 按位與 | 左→右 | 2 |
| 9 | `^` | 按位異或 | 左→右 | 2 |
| 10 | `|` | 按位或 | 左→右 | 2 |
| 11 | `&&` | 邏輯與 | 左→右 | 2 |
| 12 | `||` | 邏輯或 | 左→右 | 2 |
| 13 | `?:` | 條件運算符 | 右→左 | 3 |
| 14 | `=` `+=` `-=` `*=` `/=` `%=` `<<=` `>>=` `&=` `^=` `|=` | 賦值及複合賦值 | 右→左 | 2 |
| 15 | `,` | 逗號運算符(順序求值) | 左→右 | 2 |
三大口訣速記法
口訣一:形象編碼法(原文改良版)
圓下箭頭一頓號 // 1級: () [] -> .
非凡增減富強針地長 // 2級: ! ~ ++ -- + - * & (type) sizeof
三乘除,四加減,五移位 // 3/4/5級: * / % | + - | << >>
千萬別把魚忘記 // % 與 * / 同級 (3級)
小燈大燈燈燈不等 // 6/7級: < <= > >= | == !=
爸喂魚,舅疑惑,裸雞也疑惑 // 8/9/10/11/12級: & | ^ && ||
十三姨,十四父,十五逗 // 13/14/15級: ?: =+=... ,
口訣二:分類順序法
括號成員第一
全體單目第二
乘除余三,加減四
移位五,關系六
等於不等排第七
位與異或和位或
邏輯與、邏輯或 八九十
條件高於賦值
逗號運算級最低
口訣三:助記句法
「括下箭點單目先,乘除加減移位遷。關系等於位與異,位或邏輯與或全。三目賦值逗號後,右左結合記心田。」
經典陷阱案例與解析
| 代碼 | 常見誤解 | 實際解析 | 結果 |
| `a = b = c = 1` | 語法錯誤 | `=` 右結合:`a = (b = (c = 1))` | a=b=c=1 |
| `*p++` | `*(p++)` 還是 `(*p)++`? | 後置 `++` 最高(1級)> `*`(2級) → `*(p++)` | 取值後指針+1 |
| `&a[3]` | `&(a[3])` 還是 `(&a)[3]`? | `[]` (1級) > `&` (2級) → `&(a[3])` | 第 3 元素地址 |
| `sizeof int * p` | `sizeof(int) * p` 還是 `sizeof(int * p)`? | `sizeof` (2級) > `*` (3級) → `(sizeof int) * p` | 類型大小乘 p |
| `a ? b : c = d` | `(a?b:c) = d` 還是 `a ? b : (c=d)`? | `?:` (13級) > `=` (14級) → `a ? b : (c = d)` | 三目結果不可作左值 |
| `i = 1; a[i] = i++;` | 行為未定義 (UB) | 同一序列點多次修改 `i`,C 標準未定義行為 | 編譯器相關,嚴禁 |
C vs C++ 關鍵差異
| 項目 | C | C++ |
| 新增運算符 | 無 | `::` `.*` `->*` `typeid` `dynamic_cast` `static_cast` `reinterpret_cast` `const_cast` `new` `delete` `co_await` `co_yield` |
| 條件運算符結果 | 右值(不可賦值) | 左值(若兩分支均為左值且類型兼容) |
| 逗號運算符重載 | 不可重載 | 可重載(極少用) |
| 優先級表長度 | 15 級 | 18 級(含類型轉換、成員指針等) |
編譯器實現細節
- 序列點:C 標準規定在序列點(語句結尾、`&&` `||` `?:` `,`、函數調用前後)前所有副作用必須完成。**兩次修改同一對象且無序列點隔離 = UB**。
- 求值順序:運算符優先級 ≠ 求值順序。`f() + g()` 中 `f` `g` 調用順序**未規定**,不可相互依賴副作用。
- 整數提升與常見算術轉換:運算前將窄類型提升為 `int`/`unsigned int`,再按符號/寬度轉換為公共類型,影響 `signed/unsigned` 混合比較結果。
- 常量折疊:編譯期計算常量表達式,`#define N 10; int a = N * 2;` → `int a = 20;`。
實戰防護建議
- 不確定時加括號:`(a + b) * c`、`(p->next)->val`、`(flag ? x : y) = z`,**多一對括號少一類 Bug**。
- 啟用警告:`-Wparentheses -Wsequence-point -Wlogical-op` 捕獲可疑優先級/序列點問題。
- 靜態分析:Cppcheck、Clang-Tidy `bugprone-operator-precedence`、`bugprone-assignment-in-condition`。
- 代碼風格強制:公司規範要求「所有二元運算符兩側加空格、嵌套表達式必加括號」。
運算符優先級是 C 語言的「語法交通規則」。**背熟 15 級表、會用三大口訣、遇疑必加括號、警告全開啟**——這就是征服它的全部秘訣。