引言
相信很多C语言初学者都知道形参实参的概念,但在函数调用中,仍然还有很多人会混淆,本文将借助示例代码详细解释C语言中形参实参及传值调用和引用调用。
形参&实参
函数调用中存在两类参数:形式参数及实际参数,两类参数的功能和使用甚至所占空间都不尽相同。下面本文将解释二者的异同。
什么是形参
形式参数(formal parameter),简称形参,是在函数头括号内声明的,某一函数私有的局部变量,。在函数外部中同名变量不会与之冲突。每次调用函数,会对函数头内定义的形参赋值。
如何声明带形参的函数
在使用ANSI C形式声明函数原型:
void function( char a, int b);
当函数接受参数时,函数原型用逗号分隔的列表指明参数的数量和类型。
什么是实参
实际参数(actual argument),简称实参,它是**主调函数(calling function)赋给被调函数(called function)**的具体值。它可以是常量,变量,正确的表达式,甚至是函数。但无论如何实参必须有一个具体的值以供拷贝于形参之中。
二者关系
简而言之,形参实际上是一种“空白”或是一种“占位符”,在函数调用的过程中,我们在某一已定义的函数中的括号内使用实参,此后函数将会拷贝实参至函数体内“填充”形参,并在函数体内得到使用。
至于怎样使用,本文将在后续段落进行详细描述。
传值调用&引用调用
实参的传递过程有两种不同的机制,其分别是传值调用和引用调用。
传值调用
传值调用的定义
在传值调用过程中,形参是一个局部变量,其初值为调用函数是括号内赋予的实参的值。其只是使用了实参的值,在程序运行时,其在函数体内占用另外的内存空间,不对实参造成影响。
传值调用的代码实例
#include<stdio.h>
int fun1(int a,int b){
a=2;
b=2;
}
int main(){
int i=0;
int j=0;
printf("before change:\ni=%d,j=%d\n",i,j);
fun1(i,j);
printf("after change:\ni=%d,j=%d\n",i,j);
}
其运行结果如下:
我们发现,果然,传值调用中对形参的操作不会对实参造成影响。
引用调用
引用调用的定义
在引用调用过程中,形参传递的不再只是一个值或是一个简单的结果,而是传递实参的地址。虽然形参仍然是一个占位符或是一个空白,但将实参拷贝到形参上后,它不再如传值调用一样占用额外的内存空间,而是和实参占用同一个内存空间。发生在形参上的任何改变都将与此同时体现在实参上。
引用调用的代码实例
实例1:
#include<stdio.h>
int fun3(int *a,int *b){
int m,*c;
m=0;
c=&m;
*c=*a;
*a=*b;
*b=*c;
}
int main(){
int *p,*q,s,t;
s=11;
t=24;
p=&s;
q=&t;
printf("before change:\ns=%d,t=%d\n",*p,*q);
fun3(p,q);
printf("after change:\ns=%d,t=%d\n",*p,*q);
}
其运行结果如下:
在此例中,我们调用函数时代入的实参分别是s和t的地址,因此发生引用调用,在函数中对s和t进行交换后,实参s和t的值同时也被交换。
实例2:
#include<stdio.h>
#define N 4
int fun3(int *a){
int i;
for (i=0;i<N;i++){
a[i]=0;
}
}
int main(){
int i;
int m[]={1,2,3,4};
printf("before change:\nm[4]=");
for (i=0;i<N;i++){
printf("%d ",m[i]);
}
printf("\n");
fun3(m); /*此时的m是一个数组名,同时也是数组m第一个值m[0]的地址,此时发生引用调用*/
printf("after change:\nm[4]=");
for (i=0;i<N;i++){
printf("%d ",m[i]);
}
printf("\n");
}
其运行结果如下:
我们可以清晰地看见实参数组m的值在函数内部发生了变化。