三次异或交换两个值
a = a ^ b;
b = a ^ b;
a = a ^ b;
1. 数值交换的本质
由于变量a
和变量b
在内存中实际上是以二进制存储的,即
/* a在内存中的二进制值为 0010 0010 */
char a = 34;
char b = 28;
/* b在内存中的二进制值为 0001 1100 */
我们想交换a
和b
的值,本质上是交换两者在内存中的二进制值。也就是将对应的每一个二进制位都进行交换。在这个交换的过程中,如果待交换的位相同,那么交换和不交换的结果一致;如果待交换的位不同,那么交换完后0
变成了1
,1
变成了0
,二进制表示法中,只有0
和1
两个数字,所以交换也可以理解为取反。概括而言,相同的位不变,不相同的位取反。
因此交换的步骤可以拆分为:首先寻找两个数的二进制值中相同和不同的位置,相同的位置二进制值不变,不相同的位置二进制值取反(1
取反就是0
,0
取反就是1
),这样就达到了交换的目的。举个例子,还是上面的a
和b
:
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | 十进制值 | |
---|---|---|---|---|---|---|---|---|---|
a | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 34 |
b | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 28 |
相同 | 相同 | 不同 | 不同 | 不同 | 不同 | 不同 | 相同 |
按照相同的位不变,不相同的位取反的规则操作后:
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | 十进制值 | |
---|---|---|---|---|---|---|---|---|---|
a | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 28 |
b | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 34 |
可见,经过这一番操作后,两个数交换了。这样的操作有一个前提:知道哪些位相同,哪些位不相同。也就是说,如果能得到一个对照表c
,c
中存储了a
和b
哪些位相同哪些位不同的信息,那么就可以利用相同的位不变,不相同的位取反的规则进行交换数据了,这一规则可以用异或运算来实现。
2. 异或的特性
xor
:异或,一种二进制位运算,不同为1,相同为0。
当相应位和0
异或时,假设自身为1
,则1^0=1
,也就是自身的值不变;假设自身为0,则0^0=0
,自身的值还是不变——得出结论1:
与0异或,自身不变。
当相应位和1
异或时,假设自身为0
,则0^1=1
,也就是自身的值取反;假设自身为1
,则1^1=0
,自身的值还是取反——得出结论2:
与1异或,自身取反。
那么,只要对照表c
中,相同的位用0
表示,不同的位用1
表示,a
和c
异或就能实现相同的位不变,不相同的位取反,b
和c
异或也能实现相同的位不变,不相同的位取反,两个数就交换了。
3. 三次异或交换的解释
第一次异或:a = a ^ b
为了便于阐述,我们令c=a^b
(其实就是第一次异或操作完的a
),注意观察c
中的每一位,0
对应着相同,1
对应着不同,完美实现了对照表的功能。也就是说,第一次异或操作得到了一个对照表c
。
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | 十进制值 | |
---|---|---|---|---|---|---|---|---|---|
a | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 34 |
b | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 28 |
相同 | 相同 | 不同 | 不同 | 不同 | 不同 | 不同 | 相同 | ||
a^b | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 |
第二次异或:b=b^a
此时与b
进行异或操作的a
,已经不是原始的a
了,经过第一步异或操作后,原始a
的值已经被改变成c
,因此我们可以将b=b^a
理解为b=b^c
,即原始的**b
和对照表进行异或**,这样一来=
左边的b
的值就变成了原始a
的值。
第三次异或:a=a^b
=
号右边的a
和b
此时都已不是原始的a
和b
了,此时的a
实际上是对照表c
,b
实际上是原始的a
,如此一来,=
右边的a^b
就可以解释为原始的a
和对照表进行异或操作,显然,这样得到的结果是原始的b
。
4. 结论
a = a ^ b; /* 得到对照表 */
b = a ^ b; /* 原始b和对照表异或得到原始a */
a = a ^ b; /* 原始a和对照表异或得到原始b */