C++位运算的应用
较难15位运算的妙用:用二进制小技巧解决实际问题
位运算是在二进制位(0和1)上直接进行的操作,包括按位与(&)、按位或(|)、按位异或(^)、左移(<<)、右移(>>)等。虽然看起来是底层操作,但它能帮我们快速判断奇偶、管理权限、快速乘除、交换数字……就像一把小巧的瑞士军刀,把复杂的任务变得简单高效。下面我们就来看看几个最常用的位运算小技巧,每一个都会配上生活里的例子和代码,帮你彻底搞懂。
1. 一眼看出奇偶:用按位与判断
原理:任何整数在二进制中,最低位(最后一位)如果是0,就是偶数;如果是1,就是奇数。所以我们只需把这个数和1做按位与——如果结果是1,说明最低位是1,是奇数;结果是0,就是偶数。
生活例子:就像电影院座位号,单号(1,3,5…)最低位是1,双号(2,4,6…)最低位是0。你只要看号最后一位数字是奇数还是偶数,就能知道座位是单号还是双号。
#include <iostream>
using namespace std;
int main() {
int num = 7; // 待判断的数字
if (num & 1) { // 按位与1:num & 1 相当于检查最低位
cout << "奇数" << endl; // 输出:奇数
} else {
cout << "偶数" << endl;
}
return 0;
}
常见错误:有人会写成 if (num & 1 == 0),但 == 的优先级比 & 高,实际变成 num & (1==0),永远为0,结果永远为假。正确写法是加括号:if ((num & 1) == 0) 或者直接用 if (!(num & 1))。
2. 权限管理:用一个整数掌管多个开关
原理:一个整数的不同二进制位可以代表不同的权限(或开关)。比如用最低位表示“读权限”,第二位表示“写权限”,第三位表示“执行权限”。我们给权限分配不同的值(1、2、4、8……这些正好是2的幂,每个位独立)。用按位或(|)可以“授予”权限,用按位与(&)可以“检查”是否拥有某个权限。
生活例子:就像一张校园卡,里面存着多个小标记:能否进图书馆、能否刷食堂、能否借器材。每个标记就是一位。管理员一次性把权限叠加(按位或),你刷卡时机器检查某一位(按位与)决定是否放行。
int READ = 1; // 二进制 001,表示读权限
int WRITE = 2; // 二进制 010,表示写权限
int EXEC = 4; // 二进制 100,表示执行权限
int userPerm = READ | WRITE; // 授予读和写权限,结果:001|010 = 011(3)
// 检查是否有读权限
if (userPerm & READ) {
cout << "可以读" << endl; // 输出:可以读
}
// 检查是否有执行权限
if (userPerm & EXEC) {
cout << "可以执行" << endl; // 不会输出,因为userPerm中没有EXEC位
}
常见错误:混淆了按位或(|)和逻辑或(||)。权限叠加必须用 |,而不能用 ||,因为 || 只返回 true/false,会丢失信息。另外,权限值必须是2的幂(1,2,4,8…),否则不同权限的位会重叠。
3. 快速乘除2的幂:移位比乘法更快
原理:左移一位相当于乘以2,左移n位相当于乘以2^n;右移一位相当于除以2(对正整数),右移n位相当于除以2^n。因为移位是CPU的底层指令,比乘除法快很多,尤其在一些性能敏感的场景(如游戏、嵌入式)很有用。
生活例子:假设你有5颗糖,要分给8个朋友每人一瓶可乐(相当于乘以8),可以直接把糖数左移3位(5×8=40)。或者有40颗糖,平均分给8个小组(除以8),直接右移3位(40÷8=5)。注意:右移只对正整数安全,负数右移会保留符号位(算术右移),结果不是简单的除以2。
int a = 5; // 初始数字
int b = a << 3; // 左移3位,等同于 a * 8 = 40
int c = 40 >> 3; // 右移3位,等同于 40 / 8 = 5
cout << "b = " << b << ", c = " << c << endl; // 输出:b = 40, c = 5
常见错误:对负数使用右移代替除法。比如 -5 >> 1 在多数C++实现中结果是 -3(因为向负无穷方向取整),而不是 -2。另外,移位位数不能超过类型的位数(比如int通常是32位),否则行为是未定义的。
4. 交换两个数:不用临时变量的魔术
原理:利用异或(^)的三个性质:
- a ^ a = 0(任何数和自己异或得0)
- a ^ 0 = a(任何数和0异或不变)
- 异或满足交换律和结合律
所以 x ^ y ^ y = x。通过三步异或,就能把x和y的值互换,而且不需要额外变量。
生活例子:就像两个同学交换作业本,不给第三个人帮忙。他们做三件事:先把两人的本子异或(变成混合信息),然后用混合信息跟其中一人的原本来回运算,最后各自拿到对方的本子。虽然有点绕,但很巧妙。
int x = 3, y = 5; // 交换前 x=3, y=5
x = x ^ y; // 此时 x = 3 ^ 5 = 6
y = x ^ y; // y = 6 ^ 5 = 3(原来x的值)
x = x ^ y; // x = 6 ^ 3 = 5(原来y的值)
cout << x << " " << y << endl; // 输出:5 3
常见错误:如果两个变量指向同一个内存(比如 x 和 y 是同一个变量),那么第一步异或后变成0,最后两个都变成0。所以在实际编程中,除非你确定它们不同,否则建议还是用临时变量更安全。另外,对于int类型异或交换没问题,但浮点数不能直接用异或。
完整示例:把上面技巧串起来
下面的程序会演示判断奇偶、权限管理、快速乘除和交换。你可以直接复制运行,看看结果。
#include <iostream>
using namespace std;
int main() {
// ---------- 1. 判断奇偶 ----------
int num = 7; // 待判断的数字
if (num & 1) { // 按位与1
cout << num << " 是奇数" << endl;
} else {
cout << num << " 是偶数" << endl;
}
// ---------- 2. 权限管理 ----------
int READ = 1; // 二进制 001
int WRITE = 2; // 二进制 010
int EXEC = 4; // 二进制 100
int userPerm = READ | WRITE; // 授予读和写
if (userPerm & READ) {
cout << "有读权限" << endl;
}
if (userPerm & EXEC) {
cout << "有执行权限" << endl; // 不会输出
}
// ---------- 3. 快速乘除 ----------
int a = 5; // 初始数
int b = a << 3; // 左移3位,乘8
int c = 40 >> 3; // 右移3位,除8
cout << "5 * 8 = " << b << ", 40 / 8 = " << c << endl;
// ---------- 4. 交换两数 ----------
int x = 3, y = 5;
x = x ^ y;
y = x ^ y;
x = x ^ y;
cout << "交换后: x = " << x << ", y = " << y << endl;
return 0;
}
运行结果:
7 是奇数
有读权限
5 * 8 = 40, 40 / 8 = 5
交换后: x = 5, y = 3
新手避坑指南
- 优先级陷阱:位运算符的优先级比比较运算符低,比算术运算符也低。比如
a & b == 0实际上是a & (b==0),要习惯加括号。 - 负数的右移:右移负数在不同编译器下可能结果不同,通常不要用来做除法;最好只用无符号整数或确认正数。
- 异或交换的“自我交换”:如果待交换的两个变量是同一个(如
swap(a, a)),结果会归零,要避免。 - 权限值必须是2的幂:不要用1,2,3这样的连续值,否则位会重叠,检查时就会乱。
学完这些,还能怎么玩?
位运算的应用远不止这些,你还可以:
- 用位运算表示集合(状态压缩):用一个整数的每一位表示一个元素是否存在,比如一个班级的自习座位是否有人,用32位整数就能表示32个座位。
- 置位、清零、翻转某一位:
a |= (1<<k)把第k位置1;a &= ~(1<<k)清零;a ^= (1<<k)翻转。 - 求2的幂的模:
a & (b-1)可以快速求a % b,当b是2的幂时。
如果想深入了解,可以复习“进制转换”和“按位与、或、异或”的基本运算,再试试做一些小题目,比如“用位运算判断一个数是不是2的幂”。位运算就像编程世界里的小魔术,用多了你会越来越喜欢它的简洁和效率。
例题精讲
在C++中,使用位运算进行权限管理。假设权限用整数表示:读=1,写=2,执行=4。现在有一个用户权限值为perm,以下哪个表达式可以正确判断该用户是否拥有"写"权限?
在C++中,若x为整数,表达式 (x & 1) == 0 可用于判断x是否为偶数。
以下C++函数使用位运算实现将参数x乘以2的功能,请填写空缺处的表达式。
int doubleValue(int x) {
return ___;
}阅读以下C++代码: int a = 5, b = 7; a = a ^ b; b = a ^ b; a = a ^ b; 执行结束后,a和b的值分别是多少?
在C++中,对无符号整数进行右移1位操作,相当于将该整数除以2并向下取整。