承接運算子優先級與結合性基礎,本文系統整理 C 語言六類特色運算子的語義、優先級、副作用與實戰陷阱,附完整可執行驗證代碼。
語法:expr1, expr2, ..., exprN
語義:按順序求值,整個表達式的值為最後一個子表達式的值。
優先級:最低(甚至低於賦值)。
驗證代碼:
int a=1, b=2, c;
c = (a++, a+=b, a-b); // 逗號表達式
// 步驟:a=2, a=4, 值=4-2=2
printf("a=%d, b=%d, c=%d\n", a, b, c); // 輸出: 4, 2, 2
常見用途:for 迴圈多變數初始化/遞增、宏展開串聯多語句。
運算子:< > <= >= == !=
值:C 無布林型,真=1,假=0。任何非零值在邏輯語境視為真。
參與算術運算:關係表達式結果可直接加減乘除。
int a=10, b=10, c=10;
a = b == c; // a=1 (真)
a = (b + (c++ > 2)); // c++>2 為真(1), a=10+1=11, c=11
a = b > c * 100; // 10 > 1100 假(0), a=0
優先級:算術 > 關係 > 賦值。括號可強制分組。
運算子:! (一元) && (邏輯與) || (邏輯或)
短路求值:核心特性。左運算元決定結果時,右運算元不求值(副作用不發生)。
int m=0, k;
k = (m=0) && (m=1); // 左邊為0(假),右邊不執行,m保持0, k=0
k = (m=1) && (m=0); // 左邊為1(真),執行右邊 m=0, k=0
k = (m=2) && (m=1) && (m=0); // 依序執行至最後,m=0, k=0
k = (m=0) || (m=1); // 左邊假,執行右邊 m=1, k=1
k = (m=1) || (m=0); // 左邊真,右邊不執行,m=1, k=1
k = (m=0) || (m=1) && (m=2); // 理解為 (m=0) || ((m=1) && (m=2))
// m=0 假 → 執行右邊 → m=1 真 → 執行 m=2 真 → k=1, m=2
int m=0, k=0, n;
n = k++ && m++; // k++ 為0(假),短路,m++ 不執行 → k=1, m=0, n=0
n = m++ && k++; // m++ 為0(假),短路,k++ 不執行 → m=1, k=0, n=0
語法:cond ? expr_true : expr_false
優先級:高於賦值,低於邏輯或。右結合。
實用範例:大寫轉小寫
char ch = getchar();
ch = (ch >= 'A' && ch <= 'Z') ? (ch + 'a' - 'A') : ch;
功能:編譯期計算型別/變數/表達式的位元組大小(型別為 size_t)。
語法:sizeof(type)、sizeof expr、sizeof (expr)
關鍵點:
sizeof(int*) 為指標大小(4/8),非指向物件大小sizeof("abc") = 4(含結束符),strlen("abc") = 3運算元:只能是左值(變數、解參考、陣列元素)。
結果:運算元的記憶體位址,型別為對應指標型別。
配對:&*p == p、*&x == x(x 為左值)。
int x, y=1, z;
x = ((9+6)%5 >= 9%5 + 6%5) ? 1 : 0; // (15%5=0) >= (4+1=5) → 0>=5 假 → x=0
y += z = x + 3; // z=3, y=1+3=4
x=y=z=1;
--x && ++y || z++; // --x=0(假) → 短路 || → z++ 執行 → x=0,y=1,z=2
| 層級 | 運算子 | 結合性 |
|---|---|---|
| 1 | () [] -> . | 左→右 |
| 2 | ++ -- + - ! ~ * & sizeof (type) | 右→左 |
| 3 | * / % | 左→右 |
| 4 | + - | 左→右 |
| 5 | << >> | 左→右 |
| 6 | < <= > >= | 左→右 |
| 7 | == != | 左→右 |
| 8 | & | 左→右 |
| 9 | ^ | 左→右 |
| 10 | | | 左→右 |
| 11 | && | 左→右 |
| 12 | || | 左→右 |
| 13 | ?: | 右→左 |
| 14 | = += -= *= /= %= &= ^= |= <<= >>= | 右→左 |
| 15 | , | 左→右 |
精通運算子優先級、短路求值、副作用順序,是寫出正確、可維護 C 代碼的基石。遇到複雜表達式,**加括號** 是最廉價的保險;理解 &&/|| 短路特性,能巧妙實現條件執行與安全檢查(如 p && p->foo())。