在 C 语言中,位操作(Bitwise Operation)是直接对内存中的二进制位进行处理的运算。它不需要复杂的数学计算,而是通过移动和翻转比特位来工作。
位操作在嵌入式开发、底层驱动、网络协议解析以及算法优化中非常核心,因为它执行效率极高 且能极致节省内存。
1. 六大基础位运算符
C 语言提供了 6 种位运算符,操作数必须是整型(int, char, short, long 等)或字符型。
| 运算符 | 名称 | 规则 | 典型用途 |
|---|---|---|---|
& |
按位与 | 两位均为 1,结果才为 1 | 清零、保留指定位、判断奇偶 |
| | | 按位或 | 只要有一位为 1,结果为 1 | 最核心的用途就是**"置位"** |
^ |
按位异或 | 两位不同为 1,相同为 0 | 翻转特定位、无临时变量交换数值 |
~ |
按位取反 | 0 变 1,1 变 0 | 构造掩码、取反 |
<< |
左移 | 二进制位向左移动,低位补 0 | 乘以 2 的 n 次方(快速乘法) |
>> |
右移 | 二进制位向右移动,高位补符号位或 0 | 除以 2 的 n 次方(快速除法) |
2. 核心运算规则详解
按位与 (&)
- 规则 :
1 & 1 = 1,其余情况均为0。 - 应用 :
- 清零 :
a & 0结果为 0。 - 取指定位 :例如
a & 0xFF可以保留a的最低 8 位,将高位清零。 - 判断奇偶 :
if (n & 1),如果结果为 1 则是奇数,为 0 则是偶数(因为奇数的二进制最后一位一定是 1)。
- 清零 :
按位或 (|)
- 规则 :
0 | 0 = 0,其余情况均为1。 - 应用 :常用于置位 (Set Bit)。例如,想将变量
a的第 3 位置为 1,可以使用a | (1 << 3),其他位保持不变。
按位异或 (^)
-
规则:相同为 0,不同为 1。
-
特性 :
- 任何数异或自身等于 0 (
a ^ a = 0)。 - 任何数异或 0 等于其本身 (
a ^ 0 = a)。
- 任何数异或自身等于 0 (
-
经典算法 :不使用临时变量交换两个整数 。
int x = 10, y = 20; x = x ^ y; y = x ^ y; // 此时 y 变为 10 x = x ^ y; // 此时 x 变为 20
移位运算 (<< 和 >>)
- 左移 (
<<) :a << n相当于a * 2^n。高位丢弃,低位补 0。 - 右移 (
>>) :a >> n相当于a / 2^n(向下取整)。- 逻辑右移:高位补 0(通常用于无符号数)。
- 算术右移:高位补符号位(0 或 1),以保持负数的符号(通常用于有符号数)。
3. 进阶:位段 (Bit-fields)
如果你需要在结构体中极致地节省空间(例如在嵌入式系统中),可以使用位段。它允许你指定一个成员占用多少个二进制位。
struct Flags {
unsigned int enable : 1; // 只占 1 位
unsigned int mode : 2; // 占 2 位,可表示 0-3
unsigned int error : 1; // 只占 1 位
}; // 总共只占用 4 字节(一个 int 的大小),而不是 3 个 int
注意 :位段虽然节省空间,但可移植性较差(不同编译器对位的排列顺序可能不同),且不能对位段成员取地址(&)。
4. 常见误区与"坑"
在使用位操作时,有几个非常容易出错的点,请务必注意:
-
优先级陷阱:
- 比较运算符
==的优先级 高于 按位与&。 - 错误写法 :
if (flags & 0x01 == 1)。这会被解析为flags & (0x01 == 1),导致逻辑错误。 - 正确写法 :
if ((flags & 0x01) == 1),务必加括号。
- 比较运算符
-
逻辑运算符与位运算符混淆:
&是按位与,&&是逻辑与。a & b是对每一位进行运算,结果可能非 0 但不一定是 1;a && b的结果只能是 0 或 1。
-
移位的安全边界:
- 负数移位:对负数进行右移,结果依赖于编译器实现(通常是算术右移),建议对无符号数进行移位操作以保证跨平台一致性。
- 溢出:左移时如果移出的位包含有效数据(特别是符号位),会导致未定义行为或数据错误。
- 移位数量 :移位的位数不能大于或等于数据类型的宽度(例如
int是 32 位,左移 32 位是未定义行为)。
-
补码理解:
- 计算机内部数值一律用补码 存储。进行按位取反
~操作时,要考虑到符号位的变化。例如~0的结果是-1(全 1 的补码)。
- 计算机内部数值一律用补码 存储。进行按位取反
位操作是 C 语言贴近硬件特性的体现,熟练掌握它能让你写出更高效、更底层的代码。