函数的参数和函数调用

原文出自:http://blog.chinaunix.net/uid-16875687-id-2155704.html

1 函数的参数
在调用函数时,大多数情况下主调函数和被调函数之间存在着数据传递关系。于是就用到了形式参数和实际参数,简称形参和实参。在定义函数时函数名后面括号中的变量称为“形参”,在主调函数(一般为Main)调用一个函数时,函数名后面括号中的参数(可以是一个表达式)称为“实参”。
例如:
void func(int x,int y)
{
…..
}/x、y为形参/
void main()
{
………
int a,b;
func(a,b);/a、b为实参/
……..
}
关于形参和实参的说明:
(1)在定义函数中指定的形参在未出现函数调用时,它们并不占用内存中的存储单元。只有在发生函数调用时,形参才被分配到内存单元。在调用结束后,形参所占用的内存单元也被释放。
(2)实参可以使常量、变量或者表达式,但要求它们必须有确定的值。在调用时将实参的值赋给形参(如果形参是数组名,则传递的是数组的首地址而不是数组的值)
(3)在被定义的函数中,必须指定形参的类型
(4)实参与形参的类型必须相同或赋值兼容
(5)c语言规定,实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实参。在调用函数时,给形参分配存储单元并将实参对应的值传递给形参(实际上是对实参的拷贝),调用结束后,形参单元被释放,实参单元容保留并维持原值。因此,在执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数实参的值。
2 数组作为函数参数
数组名作为形参或实参,传递的是数组的首地址。另外,数组元素可作为函数的实参
注意:
(1)用数组名作为函数参数,应该在主调函数和被调用函数分别定义数组,不能只在一方定义
(2)实参数组与形参数组类型应一致,如不一致,结果将出错。
(3)在被调用函数中声明形参数组的大小是不起作用的,在实际中c编译对形参数组的大小是不做检查的,只是将实参数组的首地址传给形参数组。
(4)形参数组也可不指定大小,在定义数组时在数组后面跟上一个空的方括号。有时候。为了在被调用函数中处理数组元素的需要,可以另设一个参数,传递需要处理的数组元素的个数。
(5)用数组名作为函数实参时,不是把数组元素的值传递给形参,而是把实参数组的起始地址传递给形参数组,这样两个数组共同占用一段内存单元。由此可以看出,形参数组中各元素的值如发生变化会使实参数组元素的值同时发生变化。
(6)形参数组名在c编译时被当作指针变量来处理
例如:形参数组:f(int array[],int n)
在编译时将array按指针变量处理,相当于:f(int *array,int n)
(7)实参数组名代表一个固定的地址,或者说是指针型常量,而形参数组并不是一个固定的地址值。作为指针变量,在函数调用开始时,它的值等于实参数组起始地址,但在被调用函数执行期间,它可以被再赋值
3 用多维数组名作函数参数
可以用多维数组名作为实参和形参,在被调用函数中对形参数组定义时可以制定每一维的大小,也可以省略第一维的大小说明,c编译不检查第一维数组的大小
注意:
(1)不能把第二维以及其他高维的大小说明忽略
(2)在第二维大小相同的前提下,形参数组的第一维可以与实参数组不同
例如:实参数组定义为:int score[5][10]
而形参数组定义为:int array[3][10]或者int array[8][10];
4 指针变量作为函数参数
指针变量可以作实参也可以做形参。
为了使在函数中改变了的变量之能被函数main所用,不能采取将要改变的普通变量作为参数的办法,而应该用指针变量为函数参数,在函数执行过程中使指针变量所指向的变量值发生改变,函数调用结束后这些变量值的变化仍然保留下来。
如果想通过函数调用得到n个要改变的值,可以:
(1)在主调函数中设N个变量,用N个指针变量指向它们
(2)将指针变量作为实参,把N个变量的地址传给所调用的函数的形参
(3)通过形参指针变量,改变这N个变量的值
(4)主调函数中就可以使用这些改变了值的变量,在这一过程中,N个指针的值是固定不变得,但其对应的变量的值是改变的
5 如果由一个实参数组,想在函数中改变此数组元素的值,实参与形参的对应关系有以下4种情况:
(1)形参和实参都用数组名,如:
main() |f(int x[],int n)
{ int a[10]; | {
…… |…..
f(a,10); | }
} |
由于形参数组名接收了实参数组首元素的地址,因此可以认为在函数调用期间形参数组与实参数组公用一段内存单元,这种形式比较好理解。
(2)实参用数组名,形参用指针变量
main() |f(int *x,int n)
{int a[10]; |{
….. |……
f(a,10); |}
…. |
} |
实参a为数组名,形参x为指向整型变量的指针变量,函数开始执行时,x指向a[0],即x=&a[0]。通过x值的改变,可以指向a数组的任一元素。
(3)实参形参都用指针变量
main() |f(int *x,int n)
{int a[10],*p; |{
p=a; | ……….
… |}
f(p,10); |
… |
} |
实参P和形参x都是指针变量。先使实参指针变量p指向数组a,p的值是&a[0]。然后将p的值传给形参指针变量x,x的初始值也是&a[0]。通过x值的改变可以使x指向数组a的任一元素。
(4)实参为指针变量,形参为数组名
main() |f(int x[],int n)
{int a[10],*p; | {
p=a; |…………
….. | }
f(p,10); |
…. |
} |
实参p为指针变量,它使指针变量p指向a[0],即p=a或者p=&a[0],形参为数组名x.在编译过程中将x做为指针变量处理。实际上这和第一种方法是相同的
注意:如果使用指针变量作为实参,必须先使指针变量有确定的值,指向一个已定义的单元。
6 关于数组作为函数参数的按值传递和按地址传递
在把数组作为参数传递给函数时,有值传递(by value)和地址传递(by reference)两种方式。在值传递方式中,在说明和定义函数时,要在数组参数的尾部加上一对方括号([]),调用函数时只需将数组的地址(即数组名)传递给函数。例如,在下例中数组x[]是通过值传递方式传递给byval_func()函数的:

include

voidbyval_func(int[]); /the byval_func() function is passed an integer array by value /
void main (void);
void main (void)
{
int x[10];
int y;
/* Set up the integer array. * /
for (y=0; y<10; y++)
x[y] = y;
/* Call byval_func() ,passing the x array by value. * /
byval_func(x);
}
/* The byval_function receives an integer array by value. * /
void byval_func(int i[])
{
int y;
/* print the content: of the integer array. * /
for (y=0; y<10; y++)
printf(“%d\n”, i[y]);
}
在上例中,定义了一个名为x的数组,并对它的10个元素赋了初值。函数byval_func()的说明如下所示:
intbyval_func(int []);
参数int[]告诉编译程序byval_func()函数只有一个参数,即一个由int类型值组成的数组。在调用byval_func()函数时,只需将数组的地址传递给该函数,即:
byval_func(x);
在值传递方式中,数组x将被复制一份,复制所得的数组将被存放在栈中,然后由byval_func()函数接收并打印出来。由于传递给byal_func()函数的是初始数组的一份拷贝,因此在byval_func()函数内部修改传递过来的数组对初始数组没有任何影响。
值传递方式的开销是非常大的,其原因有这样几点:第一,需要完整地复制初始数组并将这份拷贝存放到栈中,这将耗费相当可观的运行时间,因而值传递方式的效率比较低;第二,初始数组的拷贝需要占用额外的内存空间(栈中的内存);第三,编译程序需要专门产生一部分用来复制初始数组的代码,这将使程序变大。
地址传递方式克服了值传递方式的缺点,是一种更好的方式。在地址传递方式中,传递给函数的是指向初始数组的指针,不用复制初始数组,因此程序变得精练和高效,并且也节省了栈中的内存空间。在地址传递方式中,只需在函数原型中将函数的参数说明为指向数组元素数据类型的一个指针。请看下例:

include

void conat_func(const int* );
void main (void);

void main(void)
{
int x[10];
int y;
/* Set up the integer array. * /
for (y=0; y<10; y++)
x[y] = y;
/* Call conat_func(), passing the x array by reference. */
conat_func(x);
}
/*The const_function receives an integer array by reference.
Notice that the pointer i» declared aa const, which renders
it unmodif table by the conat_funcO function. * /
void conat_func(conat int* i)
{
int y;
/ * print the contents of the integer array. * /
for (y=0; y<10; y++)
printf(“”%d\n”, *(i+y));
}

在上例中,同样定义了一个名为x的数组,并对它的10个元素赋了初始值。函数const_func()的说明如下所示:
int const_func(const int·);
参数constint·告诉编译程序const_func()函数只有一个参数,即指向一个int类型常量的指针。在调用const_func()函数时,同样只需将数组的地址传递给该函数,即:
const_rune(x);
在地址传递方式中,没有复制初始数组并将其拷贝存放在栈中,const_rune()函数只接收到指向一个int类型常量的指针,因此在编写程序时要保证传递给const_func()函数的是指向一个由int类型值组成的数组的指针。const修饰符的作用是防止const_func()函数意外地修改初始数组中的某一个元素。
地址传递方式唯一的不足之处是必须由程序本身来保证将一个数组传递给函数作为参数,例如,在函数const—rune()的原型和定义中,都没有明确指示该函数的参数是指向一个由int类型值组成的数组的指针。然而,地址传递方式速度快,效率高,因此,在对运行速度要求比较高时,应该采用这种方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值