创建函数指针数组
这个技巧就是创建一个与回复类型一一对应的函数指针数组。在此之前,我们先看看怎么创建函数指针数组。如果想在数组中保存一组函数名,可以这样写:
replies[] = {dump, second_chance, marriage};
但这样的语法在C语言中行不通,如果想在数组中保存函数,就必须告诉编译器函数的具体特征:函数返回什么类型以及接收什么参数。也就是说必须使用下面这种复杂得多的语法:
如何用数组解决刚才的问题?
观察数组,函数名的顺序与枚举类型的顺序完全相同:
- enum response_type {DUMP, SECOND_CHANCE, MARRIAGE};
这点很重要,因为当C语言在创建枚举时会给每个符号分配一个从0开始的数字,所以DUMP == 0, SECOND_CHANCE == 1
, 而 MARRIAGE == 2
,也就是说可以通过response_type
获取数 组中的函数指针。
能否用函数数组来修改之前的main()函数呢?
磨笔上阵
虽然这道题很难,但只要多花点时间应该没什么问题。补全这段代码所需的知识你都已经掌握了。在新版
main()
函数中,switch/case
语句已移除,你需要用一行代码来替代它,这行代码将从replies
数组中找到对应的函数名,然后用它来调用函数。
磨笔上阵解答
这道题很难,新版
main()
函数中,switch/case
语句已移除,你需要用一行代码来替代它,这行代码将从replies
数组中找到相应函数名,然后用它来调用函数。
我们来分解这个表达式。
试驾
当运行新版程序时,得到了和刚才一样的输出:
区别呢?现在你用下面这行代码代替了整个switch
语句:
(replies[r[i].type])(r[i]);
如果需要在程序中多次调用回复函数,你不必复制很多代码,而当决定添加新的回复类型和函数时,只需要把它加到数组中即可:
函数指针数组让代码易于管理,它们让代码变得更短、更易于扩展,从而可以伸缩。一开始理解起来有些费劲,但函数指针数组的确可以提高C编程技巧。
要点
函数指针中保存了函数的地址。
函数名其实是函数指针。1
1 并不完全等于,函数名是L-value,在存储器中不分配变量。——译者注
如果你有函数
shoot()
,那么shoot
和&shoot
都指向了shoot()
函数。可以用“返回类型(变量名)(参数类型)”来声明新的函数指针。
如果
fp
是函数指针,那么可以用fp
(参数,……)调用函数。也可以用(
\
fp
)(参数,……),两种情况都能工作。C标准库中有一个叫
qsort()
的排序函数。
qsort()
接收指向比较器函数的指针,比较器函数可以比较两个值的大小。比较器函数接收两个指针,分别指向待排序数组中的两项。
如果把数据保存在数组中,就可以用函数指针数组将函数与数据项关联起来。
这里没有蠢问题
问:为什么函数指针的语法这么复杂?
答:因为当声明函数指针时,需要说明返回类型和参数类型,这就解释了为什么有那么多的括号。
问:刚才那段代码看起来有点像其他语言中面向对象的代码,是吗?
答:的确很像,面向对象语言将一组函数(称为方法)与数据关联在一起。同样你也可以用函数指针将函数与数据关联在一起。
问:也就是说C语言也是面向对象的?太好了。
答:C语言不是面向对象语言,不过一些以C语言为基础的语言,例如Objective-C和C++,在底层使用函数指针时创建了很多面向对象的特性。