函数
C语言函数传参
C 语言中,函数的参数传递方式有两种:传值调用和传址调用。
传值调用
将实参的值复制到形参相应的存储单元中,即形参和实参分别占用不同的存储单元,这种传递方式称为“参数的值传递”或者“函数的传值调用”。
值传递的特点是单向传递,即主调函数调用时给形参分配存储单元,把实参的值传递给形参,在调用结束后,形参的存储单元被释放,而形参值的任何变化都不会影响到实参的值,实参的存储单元仍保留并维持数值不变。
传址调用
这种方式使用数组名或者指针作为函数参数,传递的是该数组的首地址或指针的值,而形参接收到的是地址,即指向实参的存储单元,形参和实参占用相同的存储单元,这种传递方式称为“参数的地址传递”。
地址传递的特点是形参并不存在存储空间,编译系统不为形参数组分配内存。数组名或指针就是一组连续空间的首地址。因此在数组名或指针作函数参数时所进行的传送只是地址传送,形参在取得该首地址之后,与实参共同拥有一段内存空间,形参的变化也就是实参的变化。
递归函数
递归的基本原理
递归函数即自调用函数,在函数内部直接或间接地自己调用自己,即函数的嵌套调用是函数本身。
- 每一级的函数调用都有自己的变量。
- 每一次函数调用都会有一次返回。
- 递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序。
- 递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反。
- 虽然每一级递归都有自己的变量,但是函数代码并不会得到复制。
- 递归函数中必须包含可以终止递归调用的语句。
递归的优缺点
其优点在于为某些变成问题提供了最简单的解决方法,简化了程序设计。
缺点是一些递归算法会很快耗尽计算机的内存资源。同时,使用递归的程序难于阅读和维护。
基本递归
例如,递归方式计算阶乘:
int fact(int n) {
if(n < 0)
return 0;
else if (n == 0 || n == 1)
return 1;
else
return n * fact(n - 1);
}
以计算4!
为例:
归方法中的需要的所有状态通过方法的参数传入下一次调用中。
- 尾递归是极其重要的,不用尾递归,函数的堆栈耗用难以估量,需要保存很多中间函数的堆栈。
尾递归实现计算阶乘:
int facttail(int n, int a)
{
if (n < 0)
return 0;
else if (n == 0)
return 1;
else if (n == 1)
return a;
else
return facttail(n - 1, n * a);
}
可变参数列表
可变参数列表可通过宏来实现,这些宏定义在stdarg.h
头文件中,它是标准库的一部分。这个头文件声明了一个类型va_list
和三个宏:va_start
,va_arg
,va_end
。可以声明一个类型为va_list
的变量,与这几个宏配合使用,访问参数的值。
参数列表的可变部分位于一个或多个普通参数(命名参数)的后面(即参数列表中至少要有一个命名参数),它在函数原型中以一个省略号表示。
例如求平均数:
#include <stdarg.h>
float average(int n_values, ...)
{
va_list var_argue;
int count;
float sum = 0;
va_start(var_argue, n_values);
for (count = 0; count<n_values; count++)
{
sum += va_arg(var_argue, int);
}
va_end(var_argue);
return sum / n_values;
}
- 函数声明一个名叫
var_argue
的变量,它用来访问参数列表的未确定部分。 var_argue
通过调用va_start
来初始化。它的第一个参数是va_list
类型变量的名字,第二个参数是省略号前最后一个有名字的参数。初始化过程把变量var_argue
设置为指向可变参数部分的第一个参数。- 为了访问参数,需要使用
va_arg
,这个宏接受两个参数:va_list
类型变量和参数列表中下一个参数的类型。在这个例子中,所有的可变参数都是整型。va_arg
返回这个参数的值,并使var_arg
指向下一个可变参数。 - 当访问完毕最后一个可变参数之后,需要调用
va_end
。
可变参使用注意事项
-
参数列表中,至少有一个命名参数,如果连一个命名参数都没有,就无法使用
arg_start
宏。 -
可变参数的类型和个数完全由程序代码控制,它并不能智能地识别不同参数的个数和类型。如果在
va_arg
中指明了错误的类型,其结果是不可预知的。 -
可变参数只能从头到尾逐个访问,如果你在访问几个参数后想中途退出,这是允许的,但是直接访问参数列表的中间部分是不行的。