C语言中两种不用空间开销的变量交换方式及易犯错误(局限性)

比较常见比较脑残的变量交换大家都知道,弄个临时变量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,自己和自己交换也难以理解并且不会发生,现在交换逻辑用函数封装,并且使用指针传参,才能真正交换外部变量,又能很好体现出会产生的问题,最后加上指针校验,才算得上是比较严谨规范的修正。

猜你喜欢

转载自blog.csdn.net/huqinweI987/article/details/50964182