见到这道题,可能你会想到小时候玩过的游戏,将分别装有酱油和醋的两个瓶子交换,醋装到酱油瓶子里,酱油装到醋瓶子里,我们都知道,交换的过程需要家住一个空瓶子,当然,我们这道题也是同样的道理,需要创建一个临时变量来辅助交换。下面看具体的代码实现:
- <span style="font-size:24px;">#include<stdio.h>
- #include<stdlib.h>//system的头文件
- int main()
- {
- int a=3;
- int b=5;
- int tmp=0;
-
- tmp=a;
- a=b;
- b=tmp;
- printf("a=%d b=%d\n",a,b);
- system("pause");
- return 0;
- }</span>
程序写完了,让我们看下运行结果吧!
程序就是按照我们的思路,交换了a和b的值,这是为什么呢?我们来分析一下。
很好理解了吧,但是,我们最好封装一个函数,避免主函数内杂乱无章,在以后的编程中,也应该保持这种良好的编程习惯。
- <span style="font-size:24px;">#include<stdio.h>
- #include<stdlib.h>
- void swap(int x,int y)
- {
- int tmp=x;
- x=y;
- y=tmp;
- }
- int main()
- {
- int a=3;
- int b=5;
- swap(a,b);
- printf("a=%d b=%d\n",a,b);
- system("pause");
- return 0;
- }</span>
再来看下运行结果:
咦,怎么封装了函数就错了呢?你知道什么原因吗?
同样的,我们可以用环境监视来说明这个问题。
a,b和x,y的值虽然一样,但其地址却不相同
调用swap函数后,x和y的值成功交换,而a,b的值没丝毫变化
通过这两幅图,我们可以清楚的看到形参只是实参的一份临时拷贝,二者在内存中的地址并不相同,这种传参方式成为传值调用,起不到我们预想的效果,我们可以进一步做下面的改进。
- <span style="font-size:24px;">#define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- #include<stdlib.h>
- void swap(int *x,int *y)
- {
- int *tmp=NULL;
- *tmp=*x;
- *x=*y;
- *y=*tmp;
- }
- int main()
- {
- int a=3;
- int b=5;
-
- swap(&a,&b);
- printf("a=%d b=%d\n",a,b);
- system("pause");
- return 0;
- }</span>
上面说传值调用达不到我们预想的效果,那有人可能会想到这种做法,那么结果如何呢?让我们拭目以待。
程序崩溃了,你很好奇为什么,其实你犯了一个天大的错误,待我给你细细道来。
这样就一清二楚了吧,以前我看过一个网上教程,总说一句“No pic you say a JB.”我总是理解不了什么意思,现在总算理解了,嘻嘻,此段话少儿不宜哦,好吧,说正事!错了这么多,这下总该对了吧。
- <span style="font-size:24px;">#include<stdio.h>
- #include<stdlib.h>
- void swap(int *x,int *y)
- {
- int tmp=0;
- tmp=*x;
- *x=*y;
- *y=tmp;
- }
- int main()
- {
- int a=3;
- int b=5;
-
- swap(&a,&b);
- printf("a=%d b=%d\n",a,b);
- system("pause");
- return 0;
- }</span>
看下结果:
目的是达到了,但是如果在面试过程中,面试官说不允许创建临时变量,那么你还会吗?
- <span style="font-size:24px;">#include<stdio.h>
- #include<stdlib.h>
- void swap(int *x,int *y)
- {
- *x=*x+*y;
- *y=*x-*y;
- *x=*x-*y;
- }
- int main()
- {
- int a=3;
- int b=5;
-
- swap(&a,&b);
- printf("a=%d b=%d\n",a,b);
- system("pause");
- return 0;
- }</span>
结果:
鉴于这道题很简单,不封装函数可能看的更清楚,下面我们就直接在主函数中完成交换了,但是请务必记得,如果函数比较复杂,一定要用函数封装,看起来简洁明了,也保持了主函数的干净度,一种很好的编程习惯,不要忘记哦!
- <span style="font-size:24px;">#include<stdio.h>
- #include<stdlib.h>
-
- int main()
- {
- int a=3;
- int b=5;
-
- a=a+b;
- b=a-b;
- a=a-b;
-
- printf("a=%d b=%d\n",a,b);
- system("pause");
- return 0;
- }</span>
原理:
是的,通过我们思考之后,你会发现其实简单的加减法就可以帮我们搞定这件事,但是回过头想想,既然加减法可以,那么乘除法行不行呢?让我们试一试。
你有没有想到余数的问题呢,有人可能想如果除不尽,小数部分丢掉了不就达不到效果了吗?在这里,我先表扬你一下,想法很好,善于动脑才是好孩子嘛,对不对,但是,请看我们的代码。
- <span style="font-size:24px;">#include<stdio.h>
- #include<stdlib.h>
-
- int main()
- {
- int a=3;
- int b=5;
-
- a=a*b;
- b=a/b;
- a=a/b;
-
- printf("a=%d b=%d\n",a,b);
- system("pause");
- return 0;
- }</span>
你会发现先做的是乘法,既然a保存的是它俩的公倍数,怎么可能除不尽呢,对吧!
再看看结果:
依然正确,但是,这两种做法有没有弊端呢?答案是肯定的,要不然我还说它干嘛,
比如我们其中一个数是0或者-1,会出现什么结果呢?
加减法输入-1:
咦,好着呢呀,所以你感觉没问题,那么你就大错特错了,我用的是VS2010,算是比较高版本的编译器吧,可能对程序进行了优化,让我们进入VC6.0再看看结果:
虽然没有语法错误,编译链接都可以通过,但是却执行不了,为什么呢,因为你的做法导致了数据溢出。
再看看乘除法输入0:
分析一下:
既然上面的两种方法都有相应的弊端,那么有没有一种更完美的办法呢,有人很聪明,想到了异或运算符,异或嘛,相异而或,相同为0,相异为1.
下面讲一下为什么它能克服上面的问题。
还有一种扩展方法,知道有这种方法即可,在此不做分析,有兴趣的自行研究
- <span style="font-size:24px;">#include<stdio.h>
- #include<stdlib.h>
-
- int main()
- {
- int a=3;
- int b=5;
- int tmp=0;
-
- tmp = a;
- a=((__int64)( (__int64) b<<32 | (a = b) ) >> 32);
- b=((__int64)( (__int64) tmp<<32 | (b = tmp) ) >> 32);
-
-
- printf("a=%d b=%d\n",a,b);
- system("pause");
- return 0;
- }</span>
结果:
做到这里,这道题算是完美完成了,但是你还有兴趣的话,可以考虑下怎样交换不同类型的数,下面我们给出一种实现方法。
- <span style="font-size:24px;">#include<stdio.h>
- #include<stdlib.h>
- #define TYPE float
- #define SWAP(type,a,b){type tmp; tmp=a; a=b; b=tmp;}
-
- int main()
- {
-
- TYPE a=3.4;
- TYPE b=5.5;
-
- SWAP(TYPE,a,b);
- printf("a=%f b=%f\n",a,b);
- system("pause");
- return 0;
- }</span>
运行结果: