第7章 函数
7.1 简介
7.2 收获
7.2.1 函数名风格
把函数类型和函数名分写两行纯属风格问题
该写法可使我们在使用视觉或某些工具程序追踪源代码时更容易查找函数名
1 | int * |
7.2.2 函数原型
使用原型最方便(且最安全)的方法是把原型置于一个单独的文件;
需要时就#include
7.2.3 函数参数——缺省参数提升
default argument promotion
在参数传递之前,char和short类型的参数被提升为int类型,float类型的参数被提升为double类型。
7.2.4 ADT和黑盒
C语言可以用于设计和实现抽象数据类型(ADT, abstract data type),因为它可以限制函数和数据定义的作用域。这个技巧也被称为黑盒(black box)设计。
- 模块的用户并不需要知道模块实现的任何细节,而且除了那些定义好的接口之外,用户不能以任何方式访问模块
- 限制对模块的访问是通过
static
关键字的合理使用实现的。它可以限制对那些并非接口的函数和数据的访问
7.2.5 递归
A 递归的特点
- 螺旋状while循环
- 直观显示
- 堆栈非常适合于实现递归,所以许多编译器使用堆栈来实现递归
B 递归的缺点
- 有时效率很低,低到令人发指
C 追踪递归函数
递归函数从直观上去理解有些困难,因为,初学者对递归函数会产生函数永远不会终止的错觉
1 | // 接受一个整型值(无符号),把它转换成字符并打印它,前导零被删除 |
使用堆栈的方式去追踪递归函数
D 递归与迭代
递归存在效率过低的问题,最著名的例子就是斐波那契数的计算,效率超级低,基本上能用递归实现的,都能用迭代代替。
1 | long |
使用递归方式实现一个函数前,先问问你自己使用递归带来的好处是否抵得上它的代价
7.2.6 可变参数列表
C语言是有可变参数列表的,通过宏
实现。stdarg宏
- va_start
- va_arg
- va_end
7.2.7 警告的总结
- 错误地在其他函数的作用域内编写函数原型
- 没有为那些返回值不是整型的函数编写原型
- 把函数原型和旧式风格的函数定义混合使用
- 在va_arg中使用错误的参数类型,导致未定义的错误
7.2.8 编程好习惯
- 在函数原型中使用参数名,可以给使用该函数的用户提供更多的信息
- 抽象数据类型可以减少程序对模块实现细节的依赖,从而提高程序的可靠性
- 当递归定义清晰的优点可以补偿它的效率开销时,就可以使用这个工具