C++按位异或 ^ 和按位取反 ~
困难25找不同与反转镜:C++ 按位异或 ^ 和按位取反 ~
在编程中,我们有时需要直接操作数字的二进制位——每一个“0”和“1”就像电路开关一样。今天我们要学习两个好玩的位运算操作符:异或(^) 和 取反(~)。
异或可以帮你“找出两个数字的不同之处”,而取反就像是把数字的每一个位都“翻个面”。它们虽然简单,但在加密、交换变量、设置标记位时特别有用。下面我们边玩边学。
1. 找不同游戏:按位异或 ^
异或的规则只有一句话:相同为0,不同为1。
想象你在玩“找不同”游戏:对比两幅图,如果某个位置图案一样,就在格子里打“×”(0);如果不一样,就打“√”(1)。异或也是这么干的——它把两个整数的二进制位逐位对比。
举例说明
假设有两个数字:
a = 6,二进制是110b = 3,二进制是011
把它们对齐后逐位比较:
| 位位置(从右往左) | 第0位 | 第1位 | 第2位 |
|---|---|---|---|
| a | 0 | 1 | 1 |
| b | 1 | 1 | 0 |
| a ^ b | 1 | 0 | 1 |
- 第0位:0和1不同 → 结果1
- 第1位:1和1相同 → 结果0
- 第2位:1和0不同 → 结果1
所以结果是二进制 101,也就是十进制 5。
代码实现很简单:
#include <iostream>
using namespace std;
int main() {
int a = 6; // 二进制 110
int b = 3; // 二进制 011
int c = a ^ b; // 异或运算,结果 101(十进制5)
cout << "a ^ b = " << c << endl; // 输出 5
return 0;
}
生活中的比喻
- 羽毛球比赛:两位选手动作相同(都正手或都反手)时不得分(0),动作不同(一个正手一个反手)时才得分(1)。
- 双胞胎找不同:你和双胞胎同学穿同款校服,如果今天都穿白袜子,那就是“相同”;如果一个人穿白袜子、一个人穿黑袜子,那就是“不同”。
2. 异或的魔法性质:两次异或回原数
异或有一个非常神奇的性质:连续两次异或同一个数,会变回原来的数。
- 公式:
a ^ b ^ b == a - 为什么呢?因为第一次异或得到“不同”的标记,第二次再和b异或时,原先不同的位会被再次翻转,正好回到原来的样子。
这个性质有什么用?
- 简单加密:假设你想把数字
x变成“密文”,可以用一个密钥key做异或:y = x ^ key。别人看到y不知道原数,但你知道再用key异或一次就能恢复:x = y ^ key。 - 交换两个变量的值(不用临时变量):
不过这种方法在实际编程中不常用(因为可读性差),但作为智力题很有趣。
int x = 5; // 第一个数 int y = 10; // 第二个数 x = x ^ y; // 步骤1:x 变成 5^10 y = x ^ y; // 步骤2:y 变成 (5^10)^10 = 5(恢复了原 x) x = x ^ y; // 步骤3:x 变成 (5^10)^5 = 10(恢复了原 y) // 现在 x=10,y=5,完美交换!
生活比喻:静音键
电视遥控器上的“静音”键原理和异或一样:按一下,声音从有变无(第一次异或);再按一下,声音从无变有(第二次异或)。你连续按两次静音,电视音量会回到原来的状态。
3. 反转镜:按位取反 ~
取反是一个“一元运算符”(只对单个数字起作用)。它的效果是把数字的每一位都翻转:0变成1,1变成0。就像照镜子,黑变成白,白变成黑。
举例说明
假设 a = 6,在32位系统中,二进制表示为:
00000000 00000000 00000000 00000110(前面全是0)
取反后变成:
11111111 11111111 11111111 11111001 —— 这是补码形式,对应的十进制是 -7。
#include <iostream>
using namespace std;
int main() {
int a = 6; // 二进制 ...0110
int d = ~a; // 取反,结果 ...1001(补码)
cout << "~a = " << d << endl; // 输出 -7
return 0;
}
为什么结果是负数?
因为 int 类型最高的那一位(第31位)是符号位,0表示正数,1表示负数。取反后,原来正数的符号位变成1,所以结果必然是负数。这个负数的值可以用公式计算:~x = -x - 1。
- 验证:
~6 = -7,因为-6 - 1 = -7。 - 再试:
~0 = -1(全1的补码)。
生活中的比喻
- 黑白反转:把一张黑白照片的底片查看,白色区域变成黑色,黑色变成白色。
- 密室开关:假设一扇门有开关(1=开,0=关),按一下取反按钮,门的状态就会完全反转。
4. 新手容易犯的常见错误
-
搞反异或和逻辑或:
- 逻辑或
||是判断“两个条件至少一个成立”,返回 true/false。 - 按位异或
^是对二进制位逐位运算,返回整数。
错误示例:if (a ^ b)本意可能是想判断a和b是否不相等?但a ^ b会产生一个数值,不等于0则条件为真。如果想判断不相等,应该用if (a != b)更直观。
- 逻辑或
-
忘记取反后数字是负数:
新手可能以为~6的结果是9(因为乍一看只看到最后几位),但实际上前面所有高位都变成了1,结果是一个很大的负数。计算时要注意 int 的位数(通常32位)。 -
异或的优先级:
异或^的优先级比关系运算符(如==、!=)低!
例如:if (a ^ b == 0)其实是先算b == 0,再算a ^ (b==0),结果可能和你想的不一样。
安全写法:if ((a ^ b) == 0)或if (a == b)。 -
用异或交换变量时遇到自己:
如果x和y指向同一个变量(比如用引用或指针时),三次异或会把值变成0。所以现实中更推荐使用临时变量交换。
5. 完整可运行示例代码
下面是一个完整的程序,演示了异或和取反的基本用法,包括加密/解密小实验和取反计算。
#include <iostream>
using namespace std;
int main() {
// ========== 基本异或 ==========
int a = 6; // 二进制 110
int b = 3; // 二进制 011
int xorResult = a ^ b; // 异或,结果为 101 (5)
cout << "a ^ b = " << xorResult << endl; // 输出 5
// ========== 异或加密/解密 ==========
int secret = 42; // 要加密的秘密数字
int key = 99; // 加密密钥(随便选)
int cipher = secret ^ key; // 加密:密文 = 秘密 ^ 密钥
cout << "明文: " << secret << ", 密文: " << cipher << endl;
int recovered = cipher ^ key; // 解密:再异或一次密钥
cout << "解密后: " << recovered << endl; // 恢复为42
// ========== 按位取反 ==========
int num = 6; // 二进制 ...0110
int flipped = ~num; // 取反,结果是 -7
cout << "~num = " << flipped << endl; // 输出 -7
// 也可以用公式验证:~x = -x - 1
cout << "验证公式:-" << num << " - 1 = " << (-num - 1) << endl; // 输出 -7
// ========== 异或交换变量(避免自赋值) ==========
int x = 50; // 第一个数
int y = 100; // 第二个数
cout << "交换前: x = " << x << ", y = " << y << endl;
x = x ^ y; // 第一步
y = x ^ y; // 第二步
x = x ^ y; // 第三步
cout << "交换后: x = " << x << ", y = " << y << endl; // x=100, y=50
return 0;
}
输出预期:
a ^ b = 5
明文: 42, 密文: 77
解密后: 42
~num = -7
验证公式:-6 - 1 = -7
交换前: x = 50, y = 100
交换后: x = 100, y = 50
6. 相关知识点指引
学完异或和取反,下一步可以了解:
- 按位与
&和按位或|:它们和异或类似,但规则不同,常用于清除/设置特定位。 - 移位运算
<<和>>:可以把二进制位向左或向右移动,快速实现乘2、除2。 - 位掩码(bitmask):用多个位表示开关状态,比如用
1<<3表示第3个开关。 - 补码:理解计算机如何存储负数(取反+1),对理解
~的结果非常有帮助。
如果你想挑战更复杂的应用,可以尝试用异或实现“只出现一次的数字”算法(LeetCode 136题),或者用取反实现“二进制反转”函数。
记住:位运算虽然看起来有点“硬核”,但一旦熟悉它们,你就能用极少的代码完成很多巧妙的操作。下次写程序时,不妨想想能不能用 ^ 或 ~ 来简化逻辑!
例题精讲
已知 int a = 5, b = 3,则表达式 (a ^ b) ^ a 的值是?
在C++中,若 unsigned char x = 0,则 ~x 的结果是 255。
下列代码使用异或交换两个整型变量 a 和 b 的值,请补全空缺部分。\nint a = 3, b = 7;\na = a ^ b;\nb = ___;\na = a ^ b;关于按位取反运算符 ~,当 int a = -1(补码表示)时,~a 的结果是?
以下代码将整数 x 的第 k 位(从0开始)取反,即若该位为0则变为1,为1则变为0。请补全空缺。\nint x = 10; // 二进制 1010\nint k = 2; // 操作第2位\nx = x ^ (___); \n// 预期结果:1010 的第2位是0,取反后为1,得到1110即14