内容简介:我们可能听过C语言中的传值和传指针,在其他语言中,也有传引用一说,那么他们到底有什么区别呢?如果你还不能准确地分辨,就该好好了解一下了。我们在初学C语言的时候就被老师教过,下面的方式是无法交换a和b的值的:运行结果如下:
我们可能听过 C语言 中的传值和传指针,在其他语言中,也有传引用一说,那么他们到底有什么区别呢?如果你还不能准确地分辨,就该好好了解一下了。
传值
我们在初学C语言的时候就被老师教过,下面的方式是无法交换a和b的值的:
#include<stdio.h>
void swap(int a,int b)
{
int temp = a;
a = b;
b = temp;
printf("swap a = %d,b = %d\n",a,b);
}
int main(void)
{
int a = 10;
int b = 20;
printf("before swap:a = %d,b = %d\n",a,b);
swap(a,b);
printf("after swap:a = %d,b = %d\n",a,b);
return 0;
}
运行结果如下:
before swap:a = 10,b = 20 internal swap a = 20,b = 10 after swap:a = 10,b = 20
可以看到,a和b的值最终并没有被交换。开始时a,b的值为10,20,而最终还是同样的值。
为什么呢?因为函数参数在传递的时候,都是传 原数据的副本 ,也就是说,swap内部使用的a和b只是最初始a和b的一个副本而已,所以无论在swap函数内部对a和b做任何改变,都不会影响初始的a和b的值。
正因如此,我们常常被告知, 不要把直接把结构体直接作为参数 ,这样效率会很低。由于结构体本身占用字节数较大,如果直接作为参数,那么将会产生一个较大的”副本“,如此一来,效率也就很低了。
我们再结合下面的图来理解:
首先图中方框中的上部分a和b代表了main函数中的a和b,即原始数据,而方框中的下部分a和b代表了函数的参数a和b,即原始数据的“副本”。(后面的图都是如此,上部分代表原始值,下部分代表函数参数值)。
调用swap函数前后的情形如下:
由于在swap中永远只是对a和b的副本进行操作,因此完全不影响原始的a和b的值。最终也不可能达到交换a和b的值的目的。
传指针
那么为解决上面的问题,我们知道,需要传指针。其代码如下:
#include<stdio.h>
void swap(int *a,int *b)
{
int temp = *a;
*a = *b;
*b = temp;
printf("swap a = %d,b = %d\n",*a,*b);
}
int main(void)
{
int a = 10;
int b = 20;
printf("before swap:a = %d,b = %d\n",a,b);
swap(&a,&b);
printf("after swap:a = %d,b = %d\n",a,b);
return 0;
}
运行结果:
before swap:a = 10,b = 20 swap a = 20,b = 10 after swap:a = 20,b = 10
可以看到在这种情况下,a,b的值才是真正交换了。
为什么又有传值,又有传指针
看到这里,不知道你是否会疑惑,为什么给函数传递参数的时候,一会是传值,一会是传指针呢?为什么传指针就能改变参数的值呢?实际上, C语言里,参数传递都是值传递! 也就是说, 你认为的传指针也是传值 ,只不过它的值是指针类型罢了。
我们再通过图来理解前面为什么传指针就可以交换a,b的值:
从图中可以看出,虽然传递给函数的是指向a和b的指针的副本,但是它的副本同样也是指向a和b,因此 虽然不能改变指针的指向,但是能改变参数a和b指向的内容,即改变原始a和b的值 。
再看传指针
如果是为指针p申请一段内存,下面的代码能达到目的吗?
#include<stdio.h>
#include<stdlib.h>
void getMemery(int *p)
{
/*申请1024个int大小*/
p = malloc(sizeof(int)*1024);
if(NULL == p)
{
printf("malloc failed\n");
p = NULL;
}
}
int main(void)
{
int *p = NULL;
getMemery(p);
printf("address of p is %p\n",p);
return 0;
}
通过前面的内容分析,肯定是达不到预期效果的。
运行结果:
address of p is (nil)
这是为什么呢?我们还是利用前面所知来分析,由于传递给getMemory函数的参数都是一个副本,因此函数内的p也是外部p的一个副本,因此即便在函数内部,将p指向了一块新申请的内存,仍然不会改变外面p的值,即p还是指向NULL。
如何修改呢?我们需要传入p的地址,即指向int类型指针的指针。
#include<stdio.h>
#include<stdlib.h>
void getMemery(int **p)
{
/*申请1024个int大小*/
*p = malloc(sizeof(int)*1024);
if(NULL == *p)
{
printf("malloc failed\n");
*p = NULL;
}
}
int main(void)
{
int *p = NULL;
getMemery(&p);
printf("address of p is %p\n",p);
free(p);
p = NULL;
return 0;
}
运行结果如下:
address of p is 0x144f010
从运行结果可以看到,p的值被改变了。
可配合下面的图进行理解:
总结
本文总结如下:
- 函数的形参都是原数据的“副本”,因此在函数内无法改变原数据
- 函数中参数都是传值,传指针本质上也是传值
- 如果想要改变入参内容,则需要传该入参的地址(指针和引用都是类似的作用),通过解引用修改其指向的内容
- 以上结论不限于C语言
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Beautiful Code
Greg Wilson、Andy Oram / O'Reilly Media / 2007-7-6 / GBP 35.99
In this unique work, leading computer scientists discuss how they found unusual, carefully designed solutions to difficult problems. This book lets the reader look over the shoulder of major coding an......一起来看看 《Beautiful Code》 这本书的介绍吧!