首先,两数交换我们通常会想到定义一个临时变量来交换,通常做法的代码如下:
#include <stdio.h>
int main()
{
int a;
int b;
int tmp;
scanf("%d%d",&a,&b);
tmp=a;
a=b;
b=tmp;
printf("%d %d\n",a,b);
return 0;
}
运行结果如下:
这些内容全部是在主函数的, 一般是不这样写的(不使用scanf),在改变一下写法用我们通常使用的函数的方法,代码如下:
void Swap_1(int a,int b)
{
int tmp;
tmp=a;
a=b;
b=tmp;
}
int main()
{
int a=10;
int b=20;
Swap_1(10,20);
printf("%d %d\n",a,b);
return 0;
}
不用说,这个函数我们期望的输出结果自然是:20 10,可是我们看看运行结果:
诶,这好像不是我们想要的结果啊?我们明明写了函数交换他们两个数字,并用在了主函数中,怎么没有改变啊?这是什么呢原因呢?原来;只是按值传递,形参的修改不会引起实参的修改!
那如何才能突破我们的函数和主函数之间的不影响的?那就要用到指针了(变量的地址)行,按照这个思想我们来写代码看看能不能实现:
void Swap_2(int *p1,int *p2) 1
{
int *tmp; 3
tmp = p1;
p1 = p2;
p2 = tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("%d,%d\n",a,b);
Swap_2(&a,&b); 13
printf("%d,%d\n",a,b); 14
return 0;
}
这个Swap_2函数里就引用了指针,我们理想结果要显示是:
10 20
20 10
运行一下是什么结果呢?
还不是我们要的结果,问题出在哪里呢?观察子函数里,我们定义了指针p1p1包括了临时指针(地址)tmp,int* p1 p2 tmp一是都是定义一个整形地址值。我们分析一下:如图所示,第1行和第13行(代码中已经标出)使得p1存放了a的地址,P2存放了b的地址,这时他们之间建立了关系,如图中虚线。来看子函数tmp = p1;p1 = p2;p2 = tmp;利用定义了的空的地址tmp p1 p2存放的内容交换了,诶,这个时候不就是p1和b,p2和a建立了联系,如图中实线连接出的。再回到主函数14行,他们交换了联系(p1存放b地址,p2存放a地址),但是显示a b好像没毛病还是原来的10 20,并没有通过地址解引用去访问呢。
那我们再来改进一下,没有接引用,那我们来解引用不就好了?加上*不就完了,代码如下所示:
void Swap_3(int *p1,int *p2)
{
int *tmp; 3
*tmp = *p1; 4
*p1 = *p2;
*p2 = *tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("%d %d\n",a,b);
Swap_3(&a,&b);
printf("%d %d\n",a,b);
return 0;
}
这次看起来改正了上面我们所犯的错误,没毛病应该没问题了,来看看运行结果吧!
这次好像更惨了,直接程序崩溃,我们看看提示这里提示我们代码中第三行int星号p是一个未初始化的值。哦对,我们都知道,在局部变量中如果没有初始化,那么他就是一个随机值了。那么有读者就会问了?Swap_2中的第三行同样的定义,它不也没有定义吗?凭什么我Swap_3中的就让我程序直接崩溃?有啥不一样的吗?明明这句话标点符号都不带差的。
通过调试我们知道了,确实他们两句没有差别,而Swap_3zai 下一句就运行不下去了,直接崩溃了,但Swap_2还是可以运行的,这显然是下一句的原因。我们看看Swap_2和Swap_3中这一句的下一行代码有什么区别呢?Swap_2 :tmp = p1;Swap_3:*tmp = *p1;。放在一起来比较一下。tmp是一个整形地址变量,我们没有初始化,没有像把&a&b放p1p2中,里面是一个随机值,这是个随机值,我们有哪里来的权限(解引用)访问他的地址所对应的值(我们并不清楚这个值什么情况)?更不要提还去赋值了。肯定会崩溃。而2中没有崩溃的原因是因为我们并没有尝试去解引用一个随机地址值对应的值!这里的tmp就是一个野指针(悬挂指针)了:你没有访问权限的地址;有可能是不存在的地址,或者存在但是你不能访问。
这么多错误的尝试,正确的解决办法呼之欲出了,那么在3上做什么修改呢?肯定大家第一个想到的不就是去掉所有tmp前面的星号,因为我们根本就没有权限啊!!好,我们再来看看代码:
void Swap_4(int *p1,int *p2)
{
int tmp;
tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("%d %d\n",a,b);
Swap_4(&a,&b);
printf("%d %d\n",a,b);
return 0;
}
就仅仅在Swap_3的基础上删掉了所有tmp的星号!看看运行结果吧:
终于成功达到目的了!!
别急 那我们再来分析分析这个代码:
tmp成了一个普通变量用来暂时存放p1解引用后对应的a值。原理还是我们文章开篇的同一个道理,不同点在哪里?不去直接a b转而是用了和他们有联系的地址变量。这不就达到了目的!!
总结:
父函数调用子函数:如果子函数的改变想要影响到父函数,那必须传指针和解引用!(多个返回值时,通常这样)
这次的分享内容就是这些,如果有看到的,希望大家多多指导!