位运算符是直接对整数类型(char, int, long 等)的二进制位(bit)进行操作的运算符。理解它们对于系统编程、嵌入式开发、性能优化和算法实现至关重要。
1. 核心概念:二进制
在深入讲解之前,请确保理解数值在内存中是以二进制形式存储的。例如:
- 十进制
5的二进制是0101(用 4 位表示) - 十进制
3的二进制是0011 - 十进制
12的二进制是1100
位运算符就在这些 0 和 1 的序列上进行操作。
2. 位运算符列表
C/C++ 提供了 6 种位运算符:
| 运算符 | 名称 | 描述 | 示例: A = 5 0101B = 3 0011 |
|---|---|---|---|
& |
按位与 | 两个位都为 1 时,结果才为 1 | A & B = 0001 (1) |
| |
按位或 | 两个位有一个为 1 时,结果就为 1 | A | B = 0111 (7) |
^ |
按位异或 | 两个位不同时,结果为 1 | A ^ B = 0110 (6) |
~ |
按位取反 | 对每一位取反,0 变 1,1 变 0 | ~A = 1010 (取决于位数,见下文) |
<< |
左移 | 将二进制位全部左移若干位,高位丢弃,低位补 0 | A << 1 = 1010 (10) |
>> |
右移 | 将二进制位全部右移若干位,低位丢弃,高位处理方式依赖类型 | A >> 1 = 0010 (2) |
3. 详细讲解与示例
我们假设使用 8 位 的 unsigned char 类型来避免符号位的复杂性。
A = 5-> 二进制0000 0101B = 3-> 二进制0000 0011
3.1. 按位与 (&)
规则:有 0 则 0,全 1 才 1。
1 | A: 0000 0101 (5) |
常见用途:
- 掩码操作:提取或检查特定位。
1 | unsigned char flags = 0b10101100; // 假设有一些标志位 |
- 判断奇偶性:
(x & 1)结果为 1 则是奇数,为 0 则是偶数。
1 | if (x & 1) { |
- 整数求余: 当
k是2的幂次时(k = 2^n),整数a % k = a & (2^k - 1)。
1 | int a = 13; // 0b 1101 (13) |
3.2. 按位或 (|)
规则:有 1 则 1,全 0 才 0。
1 | A: 0000 0101 (5) |
常见用途:
- 设置特定位为 1。
1 | unsigned char data = 0b10100000; |
3.3 按位异或 (^)
规则:相同为 0,不同为 1。
1 | A: 0000 0101 (5) |
重要性质:
x ^ x = 0(任何数与自己异或结果为 0)x ^ 0 = x(任何数与 0 异或结果为自己)- 满足交换律和结合律。
常见用途:
- 不借助临时变量交换两个数:
1 | a = a ^ b; |
- 加密/解密:使用同一个密钥异或两次可以得到原数据。
1 | char data = 'A'; |
- 翻转特定位:
1 | unsigned char data = 0b10101010; |
3.4 按位取反 (~)
规则:一元运算符,0 变 1,1 变 0。
1 | A: 0000 0101 (5) |
注意: 结果取决于操作数的类型。
- 对于
unsigned char a = 5,~a是 250。 - 对于
signed char a = 5,~a通常是 -6(在二进制补码表示法中)。
常见用途:
- 与
&运算符结合来清除位。
1 | // 清除flags的第3位(索引从0开始) |
3.5. 左移 (<<)
规则:value << n,将 value 的二进制位向左移动 n 位。高位丢弃,低位补 0。
1 | A = 5: 0000 0101 |
重要性质: 在没有溢出的情况下,左移 n 位相当于乘以 2^n。
常见用途:
- 快速计算 2 的幂次乘法。
- 用于构建位掩码。
1 | int bit_3 = 1 << 3; // 创建第3位(索引从0开始)为 1 的掩码: 0b00001000 |
3.6. 右移 (>>)
规则:value >> n,将 value 的二进制位向右移动 n 位。低位丢弃,高位的处理方式取决于值的类型。
- 对于无符号数:高位补 0(逻辑右移)。
1 | uint8_t A = 0b10101100; (172) |
- 对于有符号数:高位补符号位(算术右移)。这可以保持负数的符号。
1 | int8_t B = -8; // 二进制补码: 1111 1000 |
重要性质: 右移 n 位相当于除以 2^n 并向零取整(对于正数和无符号数)或向下取整(对于负数)。
常见用途:
- 快速计算 2 的幂次除法(对正数和无符号数):
1 | // 快速计算 a / 4 |
- 提取高位的数据:
1 | // 获取a的最高位 |
4. 复合赋值运算符
除了按位取反~,其他所有位运算符都可以和赋值号=结合,形成复合赋值运算符。
1 | a &= b; // 等价于 a = a & b; |
5. 重要注意事项
- 操作数类型:位运算符的操作数必须是整数类型(
char,int,long,long long及其unsigned版本)。不能用于float,double等浮点类型。 - 符号位:对有符号数进行位操作(尤其是右移
>>和取反~)时,要特别注意符号位的影响,这可能导致不可移植或意想不到的行为。**在需要明确进行位操作时,优先使用无符号类型 (unsigned int等)**。 - 移位位数:移位的位数
n必须是非负整数,并且小于操作数类型的位宽。例如,对int(通常是 32 位)移位,n应该在 0 到 31 之间。否则,其行为是未定义的。 - 运算符优先级:位运算符的优先级相对较低,低于算术运算符。为了代码清晰,强烈建议使用括号。
1 | // 不清楚的写法 |
6. 总结
位运算符是 C/C++ 中强大而高效的工具。通过直接操作二进制位,你可以实现精细的控制、紧凑的数据存储(位域)和高性能的算法。熟练掌握它们是你成为一名优秀 C/C++ 程序员的重要一步。多加练习,从简单的掩码操作开始,逐步尝试更复杂的应用。