函数定义
略
函数声明
当程序调用一个无法见到原型的函数时, 编译器会认为该函数返回一个整型值. 对于那些并不返回整型值的函数, 这种认定可能会引发错误.
所有的函数都应该具有原型, 尤其那些返回值不是整型的函数
函数的参数
C 函数所有参数均以 传值调用 方式进行传递, 也就是函数将获得参数值的一份 拷贝.
如果参数是一个数组名, 并在函数中通过下标修改了该数组参数, 那么么将修改调用 程序中的数组元素. 数组并不会被赋值, 这个行为被称为 传址调用.
数组名的值实际上是一个指针, 传递给函数就是这个指针的拷贝. 下标引用实际上是 间接访问的以一种形式.
- 传递给函数的标量参数都是传值调用
- 传递给函数的数组参数在行为上就像他们是通过传址调用那样.
ADT 和黑盒
由于C可以限制函数和数据定义的作用域, 所以它可以用于设计和实现 抽象数据类型(ADT, abstract data type). 这个技巧也称为 黑盒(black box) 设计.
抽象数据类型的基本想法很简单----模块具有功能说明和接扩说明, 前者说明模块 所执行的任务, 后者定义模块的作用. 模块的用户不需知道模块实现的任何细节.
限制模块的访问是通过 static 关键字的合理使用实现的, 可以对那些非接口的函数 和数据访问
递归
C 通过运行时堆栈支持递归函数的实现. 递归函数就是直接或间接的调用自身的函数
追踪递归函数
当函数调用时, 它的变量空间是创建与运行时堆栈上的. 以前调用的函数变量仍保留 在堆栈上, 但他们被新函数的变量所掩盖, 因此不能访问.
递归函数调用自身时也是如此. 每进行一次新的调用, 都将创建一批变量, 它们将 掩盖递归函数前一次调用所创建的变量.
当递归函数不在调用自身, 函数将会返回, 并开始销毁堆栈上的变量值.
递归与迭代
递归是一种强有力的技巧, 但和其他技巧一样, 它可能被误用, 必须阶乘.
递归函数将设计一些运行时开销----参数必须压到堆栈中, 为局部变量分配内存空间, 寄存器的值不惜保存等. 等递归函数每次调用返回时, 上述这些操作必须还原, 恢复 成原来的样子.
递归计算阶乘:
/* * 用递归计算 n 的阶乘 */ long factorial(int n) { if (n <= 0) return 1; else return n * factorial(n -1 ); }
迭代计算阶乘:
long factorial(int n) { int result = 1; while (n > 1){ result *= n; n -= 1; } return result; }
递归方法计算斐波那契数
long fibonacci( int n ) { if (n < = 2) return 1; return fibonacci(n - 1) + fibonacci(n - 2); }
有时候循环不如递归形式符合抽象定义, 但它的效率提高了几十万倍.
使用递归方式实现一个函数之前, 先问问自己使用递归带来的好处是否低得上它的 代价, 而且必须小心, 这个代价可能比初看上去要大的多.
long fibonacci( int n) { long result; long previous_result; long next_older_result; result = previous_result = 1; while (n > 2){ n -= 1; next_older_result = previous_result; previous_result = result; result = previous_result + next_older_result; } return result; }
可变参数列表
可变参数列表是通过宏来实现的, 这些宏定义与 stdarg.h 文件, 是标准库的一部分.
头文件声明了一个类型 va_list 和三个宏 ---- va_start, va_arg 和 va_end. 可以声明一个类型为 va_list 的变量, 与几个宏配合使用, 访问参数值.
参数列表使用省略号提示此处可能传数量和类型未确定的参数.
函数声明一个 var_arg 的变量, 用于访问参数列表未去顶的部分. 这个变量通过调用 va_start 来初始化. 它的第一个参数是 va_list 变量的名字, 第二个参数是省略号前最后一个有名字的参数. 初始化过程把 var_arg 变量设置为 指向可变参数部分的第一个参数
使用 va_arg 宏访问参数, 它接受两个参数: va_list 变量和参数列表中一个参数的 类型.
访问完毕最后一个可变参数后, 需要调用 va_end.
可变参数的限制
- 可变参数必须从头到按照顺序做个访问. 可以访问几个后半途终止.
- 这些宏无法判断实际存在的参数的数量
- 这些红无法判断每个参数的类型