第22章 位运算
| 等级 | 知识块 | 考查频率 |
|---|---|---|
| GESP C++ 三级 | 位运算(与/或/非/异或/左移/右移) | ⭐⭐⭐ |
位运算直接对整数的二进制位进行操作,速度快、效率高,广泛用于底层编程、状态压缩、加密与算法优化。三级要求掌握六种位运算符的使用方法与原理。
22.1 位运算的基本概念
位运算的操作对象是整数的二进制表示,包括 signed(有符号)和 unsigned(无符号)。运算时忽略符号的"含义",仅对每一位做逻辑/算术操作。
例如整数 5(二进制 00000101)和 3(00000011),位运算直接对每一位操作。
22.2 常用的位运算符
22.2.1 按位与(&)
两个对应位都为 1 时结果位为 1,否则为 0。 示例:
应用:清零(a & 0);截取指定位(a & 0x0F 取低 4 位);判断奇偶(a & 1)。
22.2.2 按位或(|)
两个对应位至少有一个为 1 时结果位为 1。 示例:
应用:置位(把某位设为 1),如 a | (1 << 3) 把第 3 位置 1。
22.2.3 按位异或(^)
两位不同则为 1,相同则为 0。 示例:
应用:翻转掩码对应位(a ^ 0x0F 翻转低 4 位);不借助临时变量交换两数。
22.2.4 按位取反(~)
所有位 0 变 1、1 变 0。示例:~5 在 32 位系统结果为 0xFFFFFFFA,满足公式 ~a = -a - 1。
22.2.5 左移(<<)
二进制整体左移 N 位,高位丢弃,低位补 0。 示例: 特性:无符号/正数左移 N 位 ≈ 乘 (不溢出时)。
22.2.6 右移(>>)
- 无符号
unsigned:逻辑右移,低位丢弃,高位补 0; - 有符号
signed:算术右移,低位丢弃,高位补符号位(正补 0,负补 1)。 示例:无符号 ;8 位有符号 特性:正数/无符号右移 N 位 ≈ 除 (向下取整)。
22.3 异或交换变量
#include <iostream>
using namespace std;
int main() {
int a = 5, b = 10;
cout << "交换前:a=" << a << ", b=" << b << endl;
a = a ^ b;
b = a ^ b;
a = a ^ b;
cout << "交换后:a=" << a << ", b=" << b << endl;
return 0;
}
两个变量若指向同一块内存(如 swap(x, x) 传同一引用),会全部置 0。工程上建议直接用 std::swap,此技巧仅作理解位运算原理。
22.4 位运算符优先级(从高到低)
~ > << >> > & > ^ > |
复杂表达式建议加括号,避免歧义。
#include <iostream>
using namespace std;
int main() {
int a = 5, b = 3;
int c = a & b | 1; // 先算 & :5&3=1,再 1|1=1
int d = a & (b | 1); // 先算 | :3|1=3,再 5&3=1
cout << "c=" << c << " d=" << d << endl; // c=1 d=1
return 0;
}
22.5 位运算应用场景
- 嵌入式硬件寄存器位控制;
- 图像 RGB 通道提取;
- 高效判断奇偶:
(a & 1) == 1; - 快速生成 2 的幂:
1 << n; - 加密、压缩的底层二进制处理;
- 状态压缩:用一个整数的不同 bit 表示多个布尔开关。
22.6 综合示例:用位运算实现常见技巧
#include <iostream>
using namespace std;
int main() {
int x = 12; // 二进制 1100
// 1) 判断奇偶
cout << x << (x & 1 ? " 是奇数" : " 是偶数") << endl;
// 2) 取第 k 位(k 从 0 开始)
int k = 2;
cout << "第 " << k << " 位 = " << ((x >> k) & 1) << endl; // 1100 第2位是 1
// 3) 把第 k 位置 1 / 置 0
cout << "第1位置1: " << (x | (1 << 1)) << endl; // 1110 = 14
cout << "第2位置0: " << (x & ~(1 << 2)) << endl; // 1000 = 8
// 4) 快速乘/除 2 的幂
cout << "12 << 1 = " << (x << 1) << " (相当于 x*2)" << endl;
cout << "12 >> 2 = " << (x >> 2) << " (相当于 x/4)" << endl;
return 0;
}
12 是偶数;第 2 位 = 1;第 1 位置 1 得 14;第 2 位置 0 得 8;12<<1=24;12>>2=3。这些技巧在竞赛「状态压缩」「位掩码」中极常用。
22.7 使用注意事项
- 有符号数右移会补符号位,逻辑操作优先用
unsigned; - 左移位数 ≥ 类型位宽时结果未定义(如
1 << 31对 32 位 int 是未定义行为); - 位运算可读性差,必须加注释;
- 32/64 位系统
int长度不同,跨平台注意位数差异。
1 << 31 在 32 位 int(范围 −2³¹~2³¹−1)里会溢出,属于未定义行为。需要大位移时用 long long:1LL << 40 才是安全的。
22.8 选择题练习
1. 表达式 5 & 3 的值是?(5=0101,3=0011)
A. 1
B. 7
C. 6
D. 0
点击展开参考答案与解析
答案:A
解析:0101 & 0011 = 0001 = 1,选 A。
2. 表达式 5 | 3 的值是?
A. 1
B. 7
C. 6
D. 15
点击展开参考答案与解析
答案:B
解析:0101 | 0011 = 0111 = 7,选 B。
3. 表达式 5 ^ 3 的值是?
A. 1
B. 7
C. 6
D. 2
点击展开参考答案与解析
答案:C
解析:0101 ^ 0011 = 0110 = 6,选 C。
4. 在 C++ 中,1 << 3 等价于?
A. 3
B. 6
C. 8
D. 9
点击展开参考答案与解析
答案:C
解析:1 << 3 把 1 左移 3 位 = 1000 = 8(即 ),选 C。
5. 无符号整数 16 >> 2 的结果是?
A. 4
B. 8
C. 2
D. 1
点击展开参考答案与解析
答案:A
解析:无符号右移 2 位:16(10000) >> 2 = 00100 = 4,选 A。
6. 判断一个整数 n 是否为奇数,最简洁的写法是?
A. n % 2 == 1
B. (n & 1) == 1
C. n / 2 != 0
D. n >> 1 == 0
点击展开参考答案与解析
答案:B
解析:奇数的最低位为 1,n & 1 == 1 最简洁高效(位运算比取模快),选 B。注意 n % 2 == 1 对负数不成立(负奇数取模可能是 −1),而 n & 1 对负奇数也正确。