C语言函数调用与参数传递
1、主调函数与被调函数
计算机在执行C程序时总是从main函数开始,如果遇到要调用某个函数,则主函数称为主调函数,被调用者称为被调函数。一个C程序可由一个main函数和若干个其他函数构成,main函数用来解决整个问题,它调用解决小问题的其他函数,其他函数也可以相互调用。调用者就是主调函数,被调者就是被调函数,应当注意,main函数只能由系统调用。
2、实际参数与形式参数
在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中进行函数调用时,函数名后面括弧中的参数称为实际参数,简称实参。在定义函数时函数名后面括弧中的变量名就是形式参数,简称形参。即实参出现在函数调用中,形参出现在函数定义中。主调函数通过函数调用将实参中的数据传递给被调函数的形参,从而实现函数间的数据传递。另外实参与形参进行数据传递时,系统要求实参与形参在数量、类型、顺序应严格保持一致,这一点在使用上要特别注意。
3、变量存储类型与作用域
主调函数和被调函数数据传递往往要通过变量进行,不同的变量类型影响数据的处理结果。C语言中变量按存储时分配的空间不同可以分为自动变量,寄存器变量,静态变量和外部变量。按变量的生命周期可以分为局部变量和全局变量,局部变量是在一个函数内部定义的变量,在存储器的动态存储区进行分配空间,作用域只在本函数内部有效,比如在主函数里定义的自动变量,寄存器变量,函数中的形式参数等都属于局部变量,在函数调用时,系统才为其分配存储空间,函数调用结束后,空间释放。而对于静态型局部变量是程序编译时由系统在存储器的静态存储区为其分配存储空间,函数调用结束后,空间不释放,其值要保留到程序退出。全局变量是在程序整个运行期间都要占用内存,所以它是全程有效,贯穿于主调函数与被调函数全过程,其值也要保留到程序退出为止。
4、参数传递的本质与属性
函数参数传递的过程,本质上是一种赋值过程即值传递过程,在调用函数之前,函数的每个实际参数将被复制,复制的值代替对应的形式参数。所以形参实际上得到的不是实参本身,而是实参的值或者实参所代表的值。因此,如果一个变量传递给一个函数,这个变量
在调用环境中所存储的值并不会被函数修改,所以形参的值不会反过来影响实参,即实参与形参值传递是单向性的。这两个问题是学习和理解函数参数传递的根本,很多学习C语言的人对这两个问题不是很理解,下面举例说明函数调用时参数传递过程。
4.1 数值传递
当变量为普通变量时,函数实参可以是自动局部变量,静态局部变量,数组元素,寄存器变量,结构体变量,结构体变量成员,常量等形式,函数形参为对应类型的变量,调用函数时,由系统给形参分配存储单元,存放从实参复制过来的数值。函数调用结束后,形参存储单元释放。
例题1:
#include
void func1(int x)
{++x;
printf (“%d”,x);
}
int main()
{int n=10;
func1(n);
printf (“%d”,n);
return 0 ;
}
例题1中main函数调用func1函数时,把实参n的值10(注意不是n)传给了形参x,x在func1函数中进行增1运算, 这时x的值发生了改变,但该值不能返回到实参n中,因为x是func1函数内部定义的变量,属于局部变量,调用函数时,系统为x变量在存储器的动态存储区分
配存储空间,函数调用结束后,x变量被释放,数值被清,故n值不变,体现了传值的单向性。
4.2 地址值传递
地址值传递是指实参与形参之间传递的数据是地址,与数值传递不同的是,地址值传递的是形参接收实参地址的复制值,而不是实参值本身。另外,地址值传递方式中系统不为形式参数变量分配存储空间,这一点也与数值传递方式不同。因为函数调用完成数据传递后,实参与形参拥有相同的变量地址,它们指向同一变量单元,该变量在主调函数定义时已经分配了存储空间,形参只是接收了它的一个地址值,并没有接收变量本身。根据参数类型的不同,地址值传递方式常见的有如下几种情况。
4.2.1 实参为变量地址,形参为指针
例题2:
#include
void func2( int *x,int *y)
{
x=y;
}
int main()
{ int a=2,b=3;
func2(&a,&b);
printf ("%d,%d",a,b);
return 0;
}
例题2中在主调函数中将a,b的地址值传给了形参指针x和y,在被调函数中将y值赋给了x,这时x的值发生了改变,x存放的是y的存储地址,即x指向3,但是这个指向并不能返回
到主调函数。因为这个地址值在函数调用结束后被释放,其值消失。当然,如果想在被调函数中修改主调函数中实参变量的值,需要修改指针变量x和y所指向的地址中的内容。比如将x=y改为*x=*y,即可达到修改实参变量的目的,但是必须清楚,虽然被调函数通过指针可以修改主调函数中的值,但这只是一种间接访问数据的形式而已,实参向形参传递数据的单向性是不变的。
4.2.2 实参为数组名,形参为指针
在C语言中,数组名是一个地址,而且是一个地址常量,它代表的是该数组元素的首地址,不是一个变量。当使用数组名作为实参时,实参的`值就是数组的首地址,形参指针接收的也是该数组的首地址,被调函数通过形参指针的变化来访问主调函数中数据。
例题3:
#include
void func3( int *x,int y)
{ int i;
for(i=0;i printf("%4d",*x++);
}
int main()
{ int a[5]={1,2,3,4,5},b=5;
func3(a,b);
return 0;
}
例题3中形参有两个,第一个表示形参接收一个整型类型的地址,第二个表示接收一个整型类型的数据,至于实参是不是一个指针,是不是一个整型变量,形式参数并不理会,只管数据的类型是否匹配。而从主调函数中可以看出,实参为数组名(地址)和整型数据(整型类型),符合参数传递规则。这样发生函数调用时,形参指针指向了数组a的第一个元素,通过循环程序,输出了数组a的所有元素。
这里还有一个问题,形参指针x在被调函数中的值发生了变化,这个值是不会返回给实参的,很明显,实参中的第一个参数a是数组名,代表一个地址常量,肯定不能对它进行赋值操作,这里进一步验证了实参与形参值传递是单向性的。
4.2.3 实参为数组名,形参为数组名
当实参与形参均为数组名时,这种方式跟其他的地址值传递方式是一样的,系统也是不给形参数组分配内存空间,而是将形参数组名处理成一个指针,因此形参数组并不存在。当发生函数调用时,实参数组只是把首地址赋给形参数组名。这样形参数组名也指向实参数组,两个数组共同占有一段内存空间。因此通过改变形参数组元素的值来达到改变实参数组元素的目的。
例题4:
#include
void func4(int b[5])
{
b[0]=5;
b[1]=4;
b[2]=3;
b[3]=2;
b[4]=1;
}
int main( )
{int i;
int a[5]={1,2,3,4,5};
func4(a);
for(i=0;i<5;i++)
printf("%4d",a[i]);
return 0;
}
例题中实参与形参均为数组名,调用函数时,实参数组的首地址复制后给了形参数组,使形参数组名指向了实参数组,当改变形参数组元素值时,实参元素值必然改变,因为实参数组和形参数组是同一块存储单元。
4.2.4 实参为指针,形参为数组名
结构体数组不能作为参数传递给函数 4.2.5 实参为指针,形参为指针
对于(1),(5)这两种情况比较好理解,实参为指针,其值为地址,所以形参接收的也是地址,实参与形参类型匹配,可以进行数据传递,在此不一一介绍。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论