比较常见比较脑残的变量交换大家都知道,弄个临时变量temp做中转,存储一个变量的值,最后补给另一个变量。
temp = i;
i = j;
j = temp;
这里还有两种变量交换方式,不用temp的!
注意:这里说的是函数封装型的
func(int *a,int*b);
int i = 5;
int j = 6;
func(&i,&j);
第一种是加法:
i = 5;
j = 6;
函数内:
*a = *a + *b;//11
*b = *a - *b;//5
*a = *a - *b;//6
函数外,i变6,j变5.
第二种是异或法:
i = 5;
j = 6;
函数内:
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
函数外,i变6,j变5.
转换成二进制:
i:0101
j:0110
第一次异或:
i:0011
第二次异或:
j:0101
第三次异或:
i:0110
刚好交换。
但是这两种交换方式,其实是有局限性的:
这个交换,使用场景少不了循环,循环就得判断边界,所以免不了出现变量自己和自己交换的情况。
自己和自己的情况:
int i = 5;
int *j = &i;
func(&i,&i);
//出错主要在传地址错误,虽然没人会使用func(&i,&i)来交换,但是有时候引用多,视觉上看不出来也正常
func(&i,j);
等同于
i = i + i;//10
i = i - i;//0
i = i - i;//0
是不是当场就懵逼了!
异或就不推了,因为是异或,结果当然直接就是零了。
但是并不代表两个变量相等交换结果就应该是零。
i = 5;
j = 5;
i = i + j;//10
j = i - j;//5
i = i - j;//5
异或:
i = 5;
j = 5;
i = i ^ j;//0000
j = i ^ j;//0101
i = i ^ j;//0101
根本区别就在于后者是两个变量,占用两个地址,两个地址就可以存两个值,就记忆功能,就可以达到交换目的。而一个变量,任何操作都会改变自身,没有记忆功能,所以无法完成交换!
避免途径:
1.检查好循环边界:避免多那么一次循环,但是也费脑子。
2.或者在交换之前进行下标或者地址对比:这个简单易行。
3.或者干脆不用:不过,既然咱都用上了,还是别折腾回去吧,不然不是白折腾一出么?毕竟有些题目就是要求不要空间开销的(比如很多翻转字符串的)
4.既然是函数形式提供,函数封装好检测就行了,直接检测值是否相等,相等就返回,不需要交换。这样也涵盖了地址相同的情况。
或者是,更好的方法是——指针检测!前者只是使用上的规避,指针检测才是函数内部的健壮性检测。
func(int *a, int *b)
{
if(a== b)
return;
//do swap
}
//20170727修正补充:之前没强调操作前提是函数封装和传指针,所以i和i,自己和自己交换也难以理解并且不会发生,现在交换逻辑用函数封装,并且使用指针传参,才能真正交换外部变量,又能很好体现出会产生的问题,最后加上指针校验,才算得上是比较严谨规范的修正。