##逻辑运算和位运算——异或
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
更通俗的讲就是:两个值不同为真,两个值相同为假。
1 | 0 | |
1 | 0(假) | 1(真) |
0 | 1(真) | 0(假) |
上面是异或在逻辑运算中的使用。但如果异或放在位运算中,就有很多的应用场景。
我们把上面的表抽取出来,看成位运算的话可以得到下面两个表,并得出两个简单结论:
1 | 0 | |
0 | 1 | 0 |
结论一:如果一个数用0进行按位异或的话,会得到这个数本身。
1 | 0 | |
1 | 0 | 1 |
结论二:如果一个数用1进行按位异或的话,会得到相反的数。也就是取反。
用上面的两个结论,我们有四种基本的异或使用环境:
使用环境一:使某些特定的位取反。
a = 10100001;
b = 00000110;
c = a ^ b; // 0b10100111
使用环境二:实现两个值的交换,而不必使用临时变量。
a = a ^ b; // a=0b10100111
b = b ^ a; // b=0b10100001
a = a ^ b; // a=0b00000110
使用环境三:将变量自身置零。
a = 10100001;
a = a ^ a; // 0b00000000
使用环境四:快速判断两个值是否相等。
if ((a ^ b) == 0)
{
//如果a和b相同则继续执行
...
}
从上面的几个基本使用方法还可以衍生出一些高级的用法。
用法一:如异或的加密运算。
#include <stdio.h>
int main()
{
char a = 'w'; // 原文
char secret = '8'; // 密钥
a = (char)(a ^ secret); // 加密
printf("%c\n", a); // 大写字母O
a = (char)(a ^ secret); // 解密
printf("%c\n", a); // w
}
这里使用了异或取反的特点。用同一个密钥(在上面代码中为变量secret),对相同的位进行异或取反两次就可以得到原文。也就是负负得正的原理,“负”一次为密文,再“负”一次就是原文了。
用法二:检测两个数不同的比特位位置。
int a = 0b10100001;
int b = 0b10101011;
int c = a ^ b; // 0b00001010
根据结果我们可以看到数a和数b,第1位和第4位的比特位是不同的。当且仅当只有一个数的某位上为1时,结果的该位才为1。否则结果的该位为0。
用法三:数a需要改变多少个比特位才能得到数b。
这是上面用法的扩展。
#include <stdio.h>
int main()
{
int a = 0b10100001;
int b = 0b10101011;
int c = a ^ b; // 0b00001010
int count = 0;
while (c)
{
c &= (c - 1);
count++;
}
printf("%d", count); // count的值为2
}
在上面代码中,数a和数b异或的结果c为0b00001010 。结果c表示,数a需要根据数c,在数c比特位为1的对应位置取反,就可以得到数b。具体到上面的代码中,数a需要在第1位和第4位的位置取反,就可以得到数b。而代码中while循环和其中count的意思是,数a需要改变2个比特位才能得到数b。