创建函数指针数组

这个技巧就是创建一个与回复类型一一对应的函数指针数组。在此之前,我们先看看怎么创建函数指针数组。如果想在数组中保存一组函数名,可以这样写:

  1. replies[] = {dump, second_chance, marriage};

但这样的语法在C语言中行不通,如果想在数组中保存函数,就必须告诉编译器函数的具体特征:函数返回什么类型以及接收什么参数。也就是说必须使用下面这种复杂得多的语法:

创建函数指针数组 - 图1

如何用数组解决刚才的问题?

观察数组,函数名的顺序与枚举类型的顺序完全相同:

  1. enum response_type {DUMP, SECOND_CHANCE, MARRIAGE};

这点很重要,因为当C语言在创建枚举时会给每个符号分配一个从0开始的数字,所以DUMP == 0, SECOND_CHANCE == 1, 而 MARRIAGE == 2,也就是说可以通过response_type获取数 组中的函数指针。

创建函数指针数组 - 图2

能否用函数数组来修改之前的main()函数呢?

创建函数指针数组 - 图3磨笔上阵

虽然这道题很难,但只要多花点时间应该没什么问题。补全这段代码所需的知识你都已经掌握了。在新版main()函数中,switch/case语句已移除,你需要用一行代码来替代它,这行代码将从replies数组中找到对应的函数名,然后用它来调用函数。

创建函数指针数组 - 图4

 

创建函数指针数组 - 图5磨笔上阵解答

这道题很难,新版main()函数中,switch/case语句已移除,你需要用一行代码来替代它,这行代码将从replies数组中找到相应函数名,然后用它来调用函数。

创建函数指针数组 - 图6

我们来分解这个表达式。

创建函数指针数组 - 图7

创建函数指针数组 - 图8试驾

当运行新版程序时,得到了和刚才一样的输出:

创建函数指针数组 - 图9

区别呢?现在你用下面这行代码代替了整个switch语句:

  1. (replies[r[i].type])(r[i]);

如果需要在程序中多次调用回复函数,你不必复制很多代码,而当决定添加新的回复类型和函数时,只需要把它加到数组中即可:

创建函数指针数组 - 图10

函数指针数组让代码易于管理,它们让代码变得更短、更易于扩展,从而可以伸缩。一开始理解起来有些费劲,但函数指针数组的确可以提高C编程技巧。

创建函数指针数组 - 图11要点

  • 函数指针中保存了函数的地址。

  • 函数名其实是函数指针。1

    1 并不完全等于,函数名是L-value,在存储器中不分配变量。——译者注

  • 如果你有函数shoot(),那么shoot&shoot都指向了shoot()函数。

  • 可以用“返回类型(变量名)(参数类型)”来声明新的函数指针。

  • 如果fp是函数指针,那么可以用fp(参数,……)调用函数。

  • 也可以用(\fp)(参数,……),两种情况都能工作。

  • C标准库中有一个叫qsort()的排序函数。

  • qsort()接收指向比较器函数的指针,比较器函数可以比较两个值的大小。

  • 比较器函数接收两个指针,分别指向待排序数组中的两项。

  • 如果把数据保存在数组中,就可以用函数指针数组将函数与数据项关联起来。

 

这里没有蠢问题

问:为什么函数指针的语法这么复杂?

:因为当声明函数指针时,需要说明返回类型和参数类型,这就解释了为什么有那么多的括号。

问:刚才那段代码看起来有点像其他语言中面向对象的代码,是吗?

:的确很像,面向对象语言将一组函数(称为方法)与数据关联在一起。同样你也可以用函数指针将函数与数据关联在一起。

问:也就是说C语言也是面向对象的?太好了。

:C语言不是面向对象语言,不过一些以C语言为基础的语言,例如Objective-C和C++,在底层使用函数指针时创建了很多面向对象的特性。