最普通的方法用临时变量的方法就不说了,下面的没有用临时变量:
int main() { int a,b; while(scanf("%d%d",&a,&b)!=EOF) { a=a+b; b=a-b; a=a-b; printf("%d %d ",a,b); } }
有些人说当a,b比较大时会溢出产生错误的结果。其实不正确,溢出虽然会溢出,但是结果任然正确。为什么?
我们假设
a,b都是short int(int类似)。
a 0x7fff 32767(2^15-1,有符号整形所能表示的最大范围)。
b 0x7fff
a=a+b后为0xfffe;(如果看成有符号数为-2(0xffff为-1)。看出无符号数为65534)。因为为signed,现在溢出了。
b=a-b; -1-32767=-32768再次溢出变成最大值32767
b=a-b类似。
可以看到,会有2次溢出。溢出规律:
SHRT_MAX+1 == SHRT_MIN
SHRT_MIN-1 == SHRT_MAX
参考:http://bbs.csdn.net/topics/390335866?page=1#post-393371903
但是结果正确。我们要劲量避免溢出。
采用异或运算:
a=a^b;
b=a^b;
a=a^b;
(可以写成一句:b ^= a ^= b ^= a; )
(
首先来看第一句:a=a^b;执行该语句后a中保存了a与b的差异位,也就是说如果原来的a和b的某一位不同,那么就将a的该位置为1,因此a在内存中成了如下图的样子,它说明a与b的第2,3个bit有差异:
接着我们来看第二句:b=b^a;其意思是,将b中有差异的位翻转,如此一来b中保存的值其实就等于原来a中的值,记住当第二个语句执行完之后a仍然保存了原来的a,b的差异信息,而b则变成了原来的a!
最后我们来看第三句:a=a^b;由于异或运算满足交换律,因此这一句等价于:a=b^a;记住这个语句赋值号右边的b中已经保存了原始的a值,而a中保存了原始的a,b的差异,因此这一句的最终作用是将原始a中有差异的位翻转(变成b)然后赋值给a,如此一来a中就保存了原始的b值。
总结:上述三句中:第一句是记录差异,第2,3句是翻转,最终实现了不用任何中间变量就交换两个变量的值。
)
可以看到右边的都是一样a^b;
举例:
a 0x7fff
b 0x7ffe
a=a^b 0x0001;
b=a^b ; 结果0x7fff
a=a^b; 结果0xfffe
交换成功。
说明一下,不要使用异或运算,增加了复杂度。
通过异或交换2个数的一个小陷阱;
可用通过异或运算交换两个数,而不需要任何的中间变量。 如下面:
- void exchange(int &a, int &b)
- {
- a ^= b;
- b ^= a;
- a ^= b;
- }
然而,这里面却存在着一个非常隐蔽的陷阱。
通常我们在对数组进行操作的时候,会交换数组中的两个元素,如exchang(&a[i], &b[j]), 这儿如果i==j了(这种情况是很可能发生的),得到的结果就并非我们所期望的。
- void main()
- {
- int a[2] = {1, 2};
- exchange(a[0], a[1]); //交换a[0]和a[1]的值
- printf("1---a[0]=%d a[1]=%d ", a[0], a[1]);
- exchange(a[0], a[0]); //将a[0]与自己进行交换
- printf("2---a[0]=%d a[1]=%d ", a[0], a[1]);
- }
上面那段测试代码的输出是:
- 1---a[0]=2 a[1]=1
- 2---a[0]=0 a[1]=1
很意外吧,第一次的交换正确的执行了,但是第二次调用exchange的时候却将a[0]置为了0. 仔细分析,不难发现,这正是我们在exchange里面用异或实现交换所造成的。如果输入a和b是同一个数,exchange里面代码相当于:
- a ^= a;
- a ^= a;
- a ^= a;
成了a做了3次(a^a^a^a^a)于自己的异或,其结果当然是0了。
既然这样,我们就不能够在任何使用交换的地方采用异或了,即使要用,也一定要在交换之前判断两个数是否已经相等了,如下:
- void exchange(int &a, int &b)
- {
- if(a == b) return; //防止&a,&b指向同一个地址;那样结果会错误。
- a ^= b;
- b ^= a;
- a ^= b;
- }
用异或来交换两个变量是错误的。参考这篇文章批判了。