首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言核心筆記(四):運算子深度解析——從逗號運算子到位元運算與優先級陷阱

     編輯:關於C

運算子分類總覽

C 語言擁有豐富的運算子集合,按功能可分為:算術、關係、邏輯、位元、賦值、條件、逗號、sizeof、取址、間接存取、下標、函式呼叫、結構成員存取等。本文聚焦**易混淆、高頻考點、實戰陷阱**的核心子集。

1. 逗號運算子:序列求值的「分號替身」

1.1 語法與語意


expr1, expr2, ..., exprN
  • **求值順序**:嚴格左到右
  • **整體值**:最後一個子表達式 `exprN` 的值
  • **優先級**:**最低**(甚至低於賦值)

1.2 經典範例


int a=1, b=2, c;
c = a + b + 3;     // 普通逗號分隔宣告,非運算子
c = (a++, a+=b, a-b); // 逗號運算子:a=2 → a=4 → 值=2
printf("%d %d %d", a, b, c); // 4 2 2

括號**必不可少**:`c = a++, a+=b, a-b;` 等價於 `(c=a++), a+=b, a-b;` → `c=1`。

1.3 實戰用途

  • `for` 迴圈多變數初始化/迭代:`for (i=0, j=10; i
  • 巨集中串聯多語句:`#define SWAP(a,b) ({typeof(a) t=a; a=b; b=t;})`(GNU 延伸)
  • 函式參數列表分隔(非運算子語境)

2. 關係運算子與關係表達式:真值的本質

2.1 C 語言的真/假

  • **假**:`0`(整數常量 0、空指標 `NULL`、`\0`、`0.0`)
  • **真**:**任意非零值**(1、-1、非空指標、非空字串...)
  • 關係運算子結果:**只產生 1 或 0**(標準保證)

2.2 關係表達式參與算術


char x='m', y='n';
int n = x < y;        // 1 ('m'=109 < 'n'=110)
n = x == y - 1;       // 1 (109 == 110-1)
n = ('y' != 'Y') + (5 < 3) + (y - x == 1); // 1 + 0 + 1 = 2

**關鍵洞察**:關係表達式結果是 `int` (0/1),可直接參與加減乘除、陣列索引等。

2.3 經典陷阱:優先級與等號混淆


int a=b=c=10;
a = b == c;  // a = (b==c) → a=1,非 a=b 再 ==c
a = (b + (c++ > 2)); // c++>2 為 1 → a = 10+1 = 11
a = b > c * 100;     // * 優先於 > → c*100=1100 → b>1100 為 0

3. 邏輯運算子:短路求值的藝術

3.1 三大運算子

運算子名稱短路規則結果型別
`!`邏輯非單目,無短路`int` (0/1)
`&&`邏輯與左側為 0 → 右側**不求值**`int` (0/1)
`||`邏輯或左側非 0 → 右側**不求值**`int` (0/1)

3.2 短路求值實戰演示


// && 短路:左側 0 即停
int m, k;
k = (m=0) && (m=1); printf("m=%d k=%d\n", m, k); // m=0 k=0 (右側未執行)
k = (m=1) && (m=0); printf("m=%d k=%d\n", m, k); // m=0 k=0 (右側執行,m變0)
k = (m=2) && (m=1) && (m=0); // m=0 k=0

// || 短路:左側非 0 即停
k = (m=0) || (m=1); // m=1 k=1
k = (m=1) || (m=0); // m=1 k=1 (右側未執行)

// 後綴遞增配合短路
m=0, k=0; int n = k++ && m++; // k=0→假,短路,m 不增 → n=0, k=1, m=0
m=0, k=0; n = m++ && k++;     // m=0→假,短路,k 不增 → n=0, m=1, k=0

3.3 混合優先級:`&&` 高於 `||`


k = (m=0) || (m=1) && (m=2); // 解析為 (m=0) || ((m=1)&&(m=2)) → m=2, k=1
k = (m=2) && (m=1) || (m=0); // ((m=2)&&(m=1)) || (m=0) → m=1, k=1

**口訣**:「與綁定緊,或綁定鬆」,不確定時**加括號**。

4. 條件運算子 `?:`:唯一的三目運算子

4.1 語法與優先級


expr1 ? expr2 : expr3
  • 優先級:**高於賦值,低於邏輯/關係/算術**
  • 結合性:**右到左**
  • 求值:`expr1` 非 0 → `expr2`;為 0 → `expr3`

4.2 實用範例:大小寫轉換


char ch; scanf("%c", &ch);
ch = (ch >= 'A' && ch <= 'Z') ? (ch + 'a' - 'A') : ch;
printf("%c\n", ch);

4.3 巢狀條件運算子(右結合)


int score; char grade;
grade = score >= 90 ? 'A' :
        score >= 80 ? 'B' :
        score >= 70 ? 'C' :
        score >= 60 ? 'D' : 'F';
// 等價於
grade = score >= 90 ? 'A' : (score >= 80 ? 'B' : (score >= 70 ? 'C' : (score >= 60 ? 'D' : 'F')));

5. sizeof 運算子:編譯期/執行期大小查詢

5.1 三種形式


sizeof(int)        // 型別名需加括號
sizeof variable    // 變數名可不加括號
sizeof expression  // 表達式可不加括號,**不求值表達式**

5.2 關鍵特性

  • **不求值運算元**:`sizeof(a = b + 1)` **不執行賦值**,只回傳 `int` 大小。
  • 回傳型別:`size_t`(無號整數)
  • 陣列名作運算元:得**整個陣列大小**(而非指標大小)

5.3 經典面試題


int a[10];
int *p = a;
printf("%zu %zu", sizeof(a), sizeof(p)); // 40 8 (假設 32-bit int, 64-bit 指標)

6. 指標相關運算子:取址 `&`、間接 `*`、下標 `[]`、成員 `->` `.`

  • `&var`:取變數位址,運算元**必須為左值**(有位址的物件)
  • `*ptr`:間接存取,`ptr` 必須為指標型別
  • `ptr[i]` 等價 `*(ptr+i)`,`i[ptr]` 同理
  • `p->m` 等價 `(*p).m`

7. 混合運算實戰演練


int x, y=1, z;
x = ((9+6)%5 >= 9%5 + 6%5) ? 1 : 0; // (0 >= 4+1=5) → 0
y += z = x + 3;                     // z=3, y=1+3=4
x=y=z=1;
--x && ++y || z++;                  // --x=0→假,短路 ||,++y 不執行,z++ 執行
// x=0, y=1, z=2

8. 運算子優先級與結合性速查表(高→低)


1. 後綴: () [] -> . ++ --
2. 前綴: ++ -- + - ! ~ * & sizeof (type)
3. 乘除餘: * / %
4. 加減: + -
5. 位移: << >>
6. 關係: < <= > >=
7. 等值: == !=
8. 位元 AND: &
9. 位元 XOR: ^
10. 位元 OR: |
11. 邏輯 AND: &&
12. 邏輯 OR: ||
13. 條件: ?:
14. 賦值: = += -= *= /= %= &= ^= |= <<= >>=
15. 逗號: ,

**同優先級**依**結合性**決定順序:多為**左結合**,賦值/條件/單目為**右結合**。

9. 求值順序 vs 優先級:危險的未定義行為

  • 優先級決定**分組**,**不決定**運算元求值順序。
  • 編譯器可自由安排運算元求值順序(除 `&&` `||` `?:` `,` 外)。

經典 UB 範例


int c = 0;
c + --c;      // c 求值順序未定:可能 0+(-1)=-1 或 -1+(-1)=-2
f() + g() + h(); // 三函式呼叫順序未定,若有副作用結果不可預期

解法:引入暫存變數強制順序:


int t1 = f(), t2 = g(), t3 = h();
int sum = t1 + t2 + t3;

10. 指標運算與陣列存取的本質


int *p, *p1;
p + 2;       // 位址 + 2*sizeof(int)
p - 2;       // 位址 - 2*sizeof(int)
p2 - p1;     // 元素個數差 = (位址差) / sizeof(int)

**標準保證**:指向陣列元素的指標,可與**最後一個元素之後**的位址比較(`<`、`==`),**不可**與第一個元素之前比較。

總結

運算子是 C 語言表達計算意圖的詞彙。精通優先級、結合性、短路求值、求值順序未定義四大核心概念,能讓你寫出既正確又高效的表達式。實戰中:複雜表達式拆分成多語句、不確定優先級加括號、副作用運算元絕不共用,是避開 90% 運算子陷阱的鐵律。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved