使用异或运算实现两处内存的原地值交换

^(异或运算)与|(或)和&(与)的一个显著的不同点在于,异或运算能够保存两个位串一种关系,举个简单的例子,比如对于a=10,b=01这两个二进制数来说,他们的异或结果为11, 从这个结果上我们就可以非常直观地得出a与b在两个位上的都是“相异”的,所以如果我们知道了两个位串的异或结果(也就是两个位串的关系),和其中的一个位串,我们就能根据它们之间的关系来推导出另一个位串的结果,这个结果是确定的,比如我们知道a是10,而异或结果是11,那么我们可以推导出b只能为01。而同样对于a和b,其按位与的结果是00,假如又知道a的值为10,那么b的值即可能是00,也可能是01,也就是说b是不能确定的。同样的或运算也是不能推导出确定的b的值的。


对于异或运算,有(a^b)^a=b,可以这样理解,a^b的结果我们可以记成一个结果R,R的每一位依赖于a和b两个位串每位的异同,如果a与b的某一位相同,那么R对应的位置0,反之置1。假如,我们现在知道了R也知道了a:

1.  对于R和a对应的每一位,如果R中的位值是0,如果a的这一位上的值是0,那么R与a在这一位上的异或结果位0,如果a在这一位中的值是1,那么R与a在这一位上的异或结果是1,总的来说如果R上的某位是0,那么与a对应位的值的异或值总是与a相同的,根据异或的定义,b对应的位上的值就是R与a对应位的异或结果;


2. 考虑第二种情况,如果R中的位置时1,如果a的这一位上的值是0,那么R与a在这一位上的异或结果是1,如果a在这一位中的值是1,那么1^1=0,总的来说,如果R上的某位是1时,那么与a对应位的值的异或结果总是与a不同的,根据异或的定义,R与a的异或结果成功地还原了b中对应位的值。


综合考虑以上两种情况,我们可以得到(a^b)^a=b。利用异或运算的这种临时保存状态的计算特性,我们可以利用其实现内存中的两个值的交换过程,当然这样做,除了会节省一点儿内存空间外,在性能上未必有太大的差别。

void swap(int *x, int *y) {
    *y = *x ^ *y;
    *x = *x ^ *y;
    *y = *x ^ *y;
}

我们分析一下以上函数体中的三行程序:

1. 第一次的*y = *x ^ *y,这次运算将x,y两个指针所指向的32位(int表示为4个字节)的内存的每一位的关系信息保存到了y所指向的32位长的内存上。这时x所指向的内存的值依旧是原来的值。


2. 第二次的 *x = *x ^ *y 可以扩展为*x = *x ^ (*x ^ *y) = *y(此*y就是y所指向内存的最初的值),所以,这行程序执行完成后,x所指向的内存的值,就变成了最初y所指向的内存的值,而此时的y所指向的内存值,依旧是*x,*y异或的结果,也就是x与y内存段的关系信息。


3. 此时的x段的内存段的值已经是最初y内存段的值了,同样根据第二步的原理,我们根据最初的y内存段的值,和x与y内存段的关系就可以推导出最初的x内存段的值,最后将其值赋予y内存段,这样y内存段就被最初的x内存段的值覆盖了。


经过以上的三步,x与y两个内存段,就在没有中间内存帮助的条件下,实现了原地值的交换,其原理就是利用了异或运算的确定性,也就是通过a,b,R中的任何两个,就可以推导出另外一个。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值