Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

位运算符是直接对整数类型(char, int, long 等)的二进制位(bit)进行操作的运算符。理解它们对于系统编程、嵌入式开发、性能优化和算法实现至关重要。

1. 核心概念:二进制

在深入讲解之前,请确保理解数值在内存中是以二进制形式存储的。例如:

  • 十进制 5 的二进制是 0101 (用 4 位表示)
  • 十进制 3 的二进制是 0011
  • 十进制 12 的二进制是 1100

位运算符就在这些 01 的序列上进行操作。

2. 位运算符列表

C/C++ 提供了 6 种位运算符:

运算符 名称 描述 示例:
A = 5 0101
B = 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 0101
  • B = 3 -> 二进制 0000 0011

3.1. 按位与 (&)

规则:有 0 则 0,全 1 才 1。

1
2
3
    A: 0000 0101 (5)
B: 0000 0011 (3)
A & B: 0000 0001 (1)

常见用途:

  • 掩码操作:提取或检查特定位。
1
2
3
unsigned char flags = 0b10101100; // 假设有一些标志位
unsigned char mask = 0b00001111; // 掩码,只关心低 4 位
unsigned char low_bits = flags & mask; // 结果: 0b00001100 (12)
  • 判断奇偶性(x & 1) 结果为 1 则是奇数,为 0 则是偶数。
1
2
3
if (x & 1) {
// x 是奇数
}
  • 整数求余: 当 k2 的幂次时(k = 2^n),整数a % k = a & (2^k - 1)
1
2
3
int a = 13;         // 0b 1101 (13)
int k = 8; // 2^3 -1 = 0b 0111 (7)
int mod = a & 0x7; // 0b 0101 (5)

3.2. 按位或 (|)

规则:有 1 则 1,全 0 才 0。

1
2
3
    A: 0000 0101 (5)
B: 0000 0011 (3)
A | B: 0000 0111 (7)

常见用途:

  • 设置特定位为 1
1
2
3
unsigned char data = 0b10100000;
unsigned char set_bit_3 = 0b00001000; // 想让第3位(索引从0开始)变为 1
data = data | set_bit_3; // 结果: 0b10101000

3.3 按位异或 (^)

规则:相同为 0,不同为 1。

1
2
3
    A: 0000 0101 (5)
B: 0000 0011 (3)
A ^ B: 0000 0110 (6)

重要性质:

  1. x ^ x = 0 (任何数与自己异或结果为 0)
  2. x ^ 0 = x (任何数与 0 异或结果为自己)
  3. 满足交换律和结合律。

常见用途:

  • 不借助临时变量交换两个数
1
2
3
a = a ^ b;
b = a ^ b; // 此时 b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a
a = a ^ b; // 此时 a = (a ^ b) ^ a = (a ^ a) ^ b = 0 ^ b = b
  • 加密/解密:使用同一个密钥异或两次可以得到原数据。
1
2
3
4
char data = 'A';
char key = 'S';
char cipher = data ^ key; // 加密
char plain = cipher ^ key; // 解密,plain 又变回了 'A'
  • 翻转特定位
1
2
3
unsigned char data = 0b10101010;
unsigned char flip_mask = 0b00001111; // 翻转低 4 位
data = data ^ flip_mask; // 结果: 0b10100101

3.4 按位取反 (~)

规则:一元运算符,0 变 1,1 变 0。

1
2
 A: 0000 0101 (5)
~A: 1111 1010 (250 for unsigned char, -6 for signed char)

注意: 结果取决于操作数的类型。

  • 对于 unsigned char a = 5~a 是 250。
  • 对于 signed char a = 5~a 通常是 -6(在二进制补码表示法中)。

常见用途:

  • & 运算符结合来清除位。
1
2
3
4
// 清除flags的第3位(索引从0开始)
unsigned char flags = 0b10101100;
unsigned char mask = 1 << 3; // 0b00001000
flags = flags & ~mask; // 0b10100100

3.5. 左移 (<<)

规则:value << n,将 value 的二进制位向左移动 n 位。高位丢弃,低位补 0。

1
2
3
A = 5: 0000 0101
A << 1: 0000 1010 (10) // 相当于 5 * 2
A << 2: 0001 0100 (20) // 相当于 5 * 4

重要性质:没有溢出的情况下,左移 n 位相当于乘以 2^n

常见用途:

  • 快速计算 2 的幂次乘法
  • 用于构建位掩码。
1
int bit_3 = 1 << 3; // 创建第3位(索引从0开始)为 1 的掩码: 0b00001000

3.6. 右移 (>>)

规则:value >> n,将 value 的二进制位向右移动 n 位。低位丢弃,高位的处理方式取决于值的类型。

  • 对于无符号数:高位补 0(逻辑右移)。
1
2
uint8_t A = 0b10101100; (172)
A >> 2: 0010 1011 (43) // 相当于 172 / 4
  • 对于有符号数:高位补符号位(算术右移)。这可以保持负数的符号。
1
2
int8_t B = -8; // 二进制补码: 1111 1000
B >> 2: 1111 1110 (-2) // 高位补 1,结果仍是负数

重要性质: 右移 n 位相当于除以 2^n向零取整(对于正数和无符号数)或向下取整(对于负数)。

常见用途:

  • 快速计算 2 的幂次除法(对正数和无符号数):
1
2
3
// 快速计算 a / 4
uint8_t a = 25; // 0b11001 (25)
uint8_t a = a >> 2; // 0b00110 (6)
  • 提取高位的数据:
1
2
3
// 获取a的最高位
uint8_t a = 0b10101100; // 172
uint8_t b = a >> 7; // 0b00000001 (1)

4. 复合赋值运算符

除了按位取反~,其他所有位运算符都可以和赋值号=结合,形成复合赋值运算符。

1
2
3
4
5
a &= b;  // 等价于 a = a & b;
a |= b; // 等价于 a = a | b;
a ^= b; // 等价于 a = a ^ b;
a <<= n; // 等价于 a = a << n;
a >>= n; // 等价于 a = a >> n;

5. 重要注意事项

  1. 操作数类型:位运算符的操作数必须是整数类型(char, int, long, long long 及其 unsigned 版本)。不能用于float, double等浮点类型。
  2. 符号位:对有符号数进行位操作(尤其是右移 >> 和取反 ~)时,要特别注意符号位的影响,这可能导致不可移植或意想不到的行为。**在需要明确进行位操作时,优先使用无符号类型 (unsigned int 等)**。
  3. 移位位数:移位的位数 n 必须是非负整数,并且小于操作数类型的位宽。例如,对 int(通常是 32 位)移位,n 应该在 0 到 31 之间。否则,其行为是未定义的
  4. 运算符优先级:位运算符的优先级相对较低,低于算术运算符。为了代码清晰,强烈建议使用括号
1
2
3
4
5
// 不清楚的写法
if (a & b == 0) ... // 这实际上是 if (a & (b == 0)),很可能不是你的本意

// 清晰的写法
if ((a & b) == 0) ...

6. 总结

位运算符是 C/C++ 中强大而高效的工具。通过直接操作二进制位,你可以实现精细的控制、紧凑的数据存储(位域)和高性能的算法。熟练掌握它们是你成为一名优秀 C/C++ 程序员的重要一步。多加练习,从简单的掩码操作开始,逐步尝试更复杂的应用。

评论