在学习C语言的时候,我们会在子程序参数传递这个地方碰到两个难以理解的概念:“值传递”和“地址传递”。这篇文章就是专门为解释清楚这个看似“老大难”的问题而来的。首先声明,以下内容大部分不是我写的,我仅在原作上做一些补充说明。
在看下面的内容之前,你要清楚C语言中一个很基础的知识点:主调函数在调用被调函数时,会在堆栈空间(一般说“堆栈”其实指的仅仅是“栈”,“堆”和“栈”是两个不同的东西,这是要区别开的。至于为什么在表述“栈”时,要说“堆栈”而不直接说“栈”,这个是“历史遗留问题”,大家都这么说,嘿嘿!何必纠结!)内按照参数多寡开辟出相应的一段空间用于存放参数,如果你还没有搞清楚甚至没有听说过这个东东,劝君不必往下看了。
实际上,因为地址本身也可以作为一个特殊的“值”,所以地址传递也是一种特殊的值传递。只是为了强调其特殊性,故称之为“地址传递”。我们在学习过程中可以视参数的形式而区别对待,比如若参数传递的是简单数据类型的数值,则将其归类为值传递方式;若参数传递的是变量的地址,则视其为地址传递方式。
值传递过程中,被调函数的形参作为被调函数的局部变量处理,即在内存的堆栈中开辟空间以存放由主调函数放进来的实参的值,从而成为了实参的一个拷贝。值传递的特点是被调函数对形参的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
而在地址传递过程中,被调函数的形参虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址(就是说我们不是一下子就根据地址找到这个数值的,而是我们开始连这个数值的地址都不知道,但是我们知道这个数值的地址搁在哪了,我们先在那儿找到数值的地址,再根据地址找到数值,这就是“间接”的意思了),即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
下面我们用书上出现频率最高的一个程序来分别实现值传递过程和地址传递过程。
首先是值传递:
void swap(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;
printf("/n(swap):%d,%d/n",x,y);
}
void main()
{ int a,b;
scanf("%d,%d",&a,&b);
if(a<b) swap(a,b);
printf("/n(main):%d,%d/n",a,b);
}
假设我们从键盘输入两个数据:5,9,先来看一下运行结果:
(swap):9,5
(main):5,9
按照值传递的特点,我们可以很清楚的看到,虽然在swap函数中暂时使得运行结果显示了交换后的数据,即达到了交换的目的,但实际情况却是随着swap函数的结束,被作为局部参数的形参x,y以及swap函数本身的局部参数temp都将结束其生存期,在内存中的存储空间被释放。因此实参a,b的并未受到影响,依然保持原值。
从这道程序中我们也可以学到一个方法,比如有时不需要修改实参,但是又需要被调函数所作的工作能够得以体现,就可以灵活的在被调函数中使用打印语句。因此上面的程序可以改写为:
void swap(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;
printf("/n从大到小排列的顺序为:%d,%d/n",x,y);
}
main()
{ int a,b;
scanf("%d,%d",&a,&b);
if(a<b) swap(a,b);
}
在输入5,9两个数字后,得到的执行结果是:
从大到小排列的顺序为:9,5
你看这样就可以使用相对容易理解的值传递方式,巧妙的解决问题了,并得到了想要的答案,虽然我们并未真正的让主函数中的两个变量发生改变。
再来看地址传递方式,地址传递有两种形式。在理解这两种形式之前,首先要清楚C语言中的两个符号:“*”和“&”是分别是什么意思,它们的有什么样的区别和联系,用法如何,如果不甚了解,请找本C语言的教程搞明白后在往下看。
形式一:
void swap(int *p,int *q)
{ int temp;
temp=*p;
*p=*q;
*q=temp;
}
void main()
{ int a,b;
int *pointer_1=&a,*pointer_2=&
scanf("%d,%d",&a,&b);
if(a<b) swap(a,b);
printf("/n%d,%d/n",a,b);
}
假设我们从键盘输入两个数据:5,9,先来看一下运行结果:
9,5
在这个程序中用指针变量作参数,注意这地方的子程序声明是:int*p,int *q,比值传递多了两个星号。虽然传送的是变量的地址,但实参和形参之间的数据传递依然是单向的“值传递”,即调用函数不可能改变实参指针变量的值。但它不同于一般值传递的是,它可以通过指针间接访问的特点来改变指针变量所指变量的值,即最终达到了改变实参的目的。
形式二:
形式二与形式一的区别仅仅在被调用的子程序上,其他部分与形式一完全相同:
voidswap(int &p,int &q)
{ int temp;
temp=p;
p=q;
q=temp;
}
形式二把形式一中的星号“*”换成了“&”。而在子程序内部对参数p和q的使用和值传递时形式是一样的。对于形式二我们可以这样对比形式一去理解:形式一中用int*p表示传递过来的是变量的地址,在内部用*p去取这个地址中的数据,从而达到修改原来数值的目的;而形式二中用int&p表示传递过来的就是一个数值,但是这个数值并不是像值传递一样是原变量的拷贝了,而它就是原变量本身。所以内部直接修改p,也达到了修改原变量的目的。
形式二的表示方法不是很好理解,在这儿讲述主要是想让大家知道,有这么一种方式,以后碰到了不会觉得奇怪罢了,强烈建议使用形式一。
对于以上内容如有问题,请不吝赐教,非常感谢!
这一部分内容对初学者来说理解起来有一定的难度,所以在学习过程中一定要多想多练多思考,希望以上的讲解能对初学者有所帮助。