前言:位元運算——C 語言的「組合語言」面向
C 語言完整支援位元運算,直接操作位元組/字的二進制位。這使 C 能編寫驅動程式、嵌入式固件、壓縮演算法、密碼學等底層代碼。位元運算僅適用於整數型別 (含 char),不適用浮點數。
一、六大位元運算子
| 運算子 | 名稱 | 功能 | 真值表 (單位位元) |
& | 位元 AND | 兩位皆 1 結果 1 | 0&0=0, 0&1=0, 1&0=0, 1&1=1 |
| | 位元 OR | 任一位為 1 結果 1 | 0|0=0, 0|1=1, 1|0=1, 1|1=1 |
^ | 位元 XOR | 兩位不同結果 1 | 0^0=0, 0^1=1, 1^0=1, 1^1=0 |
~ | 位元 NOT | 單目,逐位取反 | ~0=1, ~1=0 |
<< | 左移 | 高位捨棄,低位補 0 | x << n 等價 x * 2^n |
>> | 右移 | 低位捨棄,高位補 0/1 | 有號數實作定義 (算術/邏輯右移) |
二、位元運算 vs 邏輯運算:關鍵區別
int x = 7; // 0111
int y = 8; // 1000
x && y // 邏輯 AND:兩者非零 → 1 (真)
x & y // 位元 AND:0111 & 1000 = 0000 → 0
- 邏輯運算結果只能是
0 或 1
- 位元運算結果可為任意整數值
- 邏輯運算有短路求值;位元運算無短路,兩邊必評估
三、經典實戰技巧
1. 位元遮罩:提取/清除/設定特定位
#define BIT(n) (1u << (n))
// 清除第 3 位 (保留其它)
x &= ~BIT(3);
// 設定第 5 位
x |= BIT(5);
// 切換第 0 位
x ^= BIT(0);
// 測試第 7 位
if (x & BIT(7)) { ... }
// 提取低 8 位
low8 = x & 0xFF;
2. 快速乘除 2 的冪次
x << 3 // x * 8
x >> 2 // x / 4 (無號數/正數)
// 負數右移行為實作定義,嚴謹場合用除法
3. 交換兩變數 (無暫存變數)
a ^= b;
b ^= a;
a ^= b;
// 僅適用整數,現代編譯器優化臨時變數同樣快,可讀性差,教學用
4. 判斷 2 的冪次
bool isPowerOfTwo(unsigned x) {
return x && !(x & (x - 1));
}
// x=8: 1000 & 0111 = 0 → true
// x=6: 0110 & 0101 = 0100 ≠ 0 → false
5. 奇偶校驗 / 計算 1 的個數 (Population Count)
int popcount(unsigned x) {
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
x = (x * 0x01010101) >> 24;
return x;
}
// 或使用內建 __builtin_popcount (GCC/Clang)
6. 大小端轉換 (32-bit)
uint32_t swapEndian(uint32_t x) {
return ((x & 0xFF000000) >> 24) |
((x & 0x00FF0000) >> 8) |
((x & 0x0000FF00) << 8) |
((x & 0x000000FF) << 24);
}
四、移位運算深度解析
| 場景 | 左移 << | 右移 >> |
| 無號數 | 低位補 0,高位捨棄 | 高位補 0 (邏輯右移) |
| 有號正數 | 同無號數 | 高位補 0 |
| 有號負數 | 同無號數 (未定義行為若溢位) | 實作定義:多數編譯器算術右移 (補符號位 1) |
| 移位量 ≥ 位寬 | 未定義行為 (C 標準) |
| 移位量 < 0 | 未定義行為 |
五、常見陷阱
| 陷阱 | 錯誤代碼 | 問題 | 修正 |
| 浮點數用位元運算 | float f; f & 1; | 編譯錯誤/未定義 | 轉整數或用 memcpy 重新解讀 |
| 有號數左移溢位 | int x = 1 << 31; | 未定義行為 (溢位) | 用無號數 1u << 31 |
| 負數右移可移植性 | int x = -8; x >> 1; | 結果視編譯器而定 | 明確需求用除法或無號數轉換 |
| 優先級陷阱 | x & 1 == 0 | 解析為 x & (1 == 0) → x & 0 | 加括號 (x & 1) == 0 |
| 巨集參數副作用 | SET_BIT(x++, 3) | x++ 執行多次 | 改用 inline 函數 |
結語
位元運算是 C 語言區別於高階語言的核心特徵。掌握「遮罩提取/設定/清除、快速乘除、2 冪次判斷、popcount、端序轉換」等實戰模式,並嚴守「無號數位元運算、移位量範圍檢查、優先級括號、避免巨集副作用」四大防線,即可在驅動開發、網路協定、圖形處理、密碼學等領域寫出高效、正確的位元級代碼。