一、指向函数的指针

函数名可以在表达式中被解读成“指向函数的指针”,因此,正如代码清单 2-2 的实验那样,写成 func 就可以取得指向函数的指针。

“指向函数的指针”本质上也是指针(地址),所以可以将它赋给指针型变量。

比如有下面的函数原型:

int func(double d);

保存指向此函数的指针的变量的声明如下:

int (*func_p)(double);

然后写成下面这样,就可以通过 func_p 调用 func,

int (*func_p)(double);  // 声明
func_p = func;  // 将func 赋给func_p
func_p(0.5);  // 此时,func_p 等同于func

将“指向函数的指针”保存在变量中的技术经常被运用在如下场合:

  • GUI 中的按钮控件记忆“当自身被按下的时候需要调用的函数”
  • 根据“指向函数的指针的数组”对处理进行分配

后者的“指向函数的指针的数组”,像下面这样使用:

int (*func_table[])(double) = {func0,func1,func2,func3,
};
┊
func_table[i](0.5);  // 调用func_table[i]的函数,参数为0.5

使用上面的写法,不用写很长的 switch case,只需通过 i 的值就可以对处理进行分配。

哦?不明白为什么?

确实,像

int (*func_p)(double);  // 指向函数的指针

还有,

int (*func_table[])(double);  // 指向函数的指针的数组

这样的声明,是不能用普通的方法来读的。

关于这种声明的解读方式,会在后面进行说明。

二、关于指向函数的指针引起的混乱

正如上面说明的那样,对于 C 语言,表达式中的函数名可以被解读成“指向函数的指针”

在信号处理、事件驱动的程序中,这种特性往往以回调函数的形式被使用。

/*如果发生SIGSEGV(Segmentation falut),回调函数segv_handler */
signal(SIGSEGV, segv_handler);

可是,如果基于之前说明过的 C 语言声明规则,int func()这样的声明会被解释为“返回 int的函数”,如果 函数名在表达式中,只是取出 func,则解释成“指向返回 int 函数的指针”,是不是感觉很怪异?如果一定要使用指向函数的指针,必须要写成&func。

对于上面信号处理的函数,写成

signal(SIGSEGV, &segv_handler);

这样,实际上也能顺利地执行

相反,像

void (*func_p)();

这样,变量 func_p 声明为指向函数的指针,进行函数调用的时候,可以写成

func_p();

但是像 int func()这种声明,都是用 func()这样的方式进行调用的,从对称性的角度考虑,对于 void (*func_p)(),必须要写成

(*func_p)();* 

* 早期的 C 语言中,好像也只能这么写……

这样也是能毫无问题地执行的。

是不是感觉 C 语言的关于指向函数的指针的语法比较混乱?

混乱产生的原因就是:“表达式中 的函数可以解读成‘指向函数的指针’”这个意图不明的规则(难道就是为了和数组保持一致?)。

为了照顾到这种混乱,ANSI C 标准对语法做了以下例外的规定:

  • 表达式中的函数自动转换成“指向函数的指针”。但是,当函数是地址运算符&或者 sizeof 运算符的操作数时,表达式中的函数名不能变换成“指向函数的指针”。
  • 函数调用运算符()的操作数不是“函数”,而是“函数的指针”。

如果对“指向函数的指针”使用解引用*,它暂时会成为函数,但是因为在表达式中,所以它会被瞬间地变回成“指向函数的指针”。

结论就是,即使对“指向函数的指针”使用*运算符,也是对牛弹琴,因为此时的运算符*发挥不了任何作用。

因此,下面的语句也是能顺利执行的,

(**********printf)("hello, world\n");  // 无论如何,*就是什么也没做

延伸阅读:

《征服 C 指针》摘录1:什么是空指针?区分 NULL、0 和 '\0'

《征服 C 指针》摘录2:C变量的 作用域 和 生命周期(存储期)

《征服 C 指针》摘录3:数组 与 指针

《征服 C 指针》摘录4:函数 与 指针

《征服 C 指针》摘录5:函数形参 和 空的下标运算符[]

《征服 C 指针》摘录6:解读 C 的声明

《征服 C 指针》摘录7:练习——挑战那些复杂的声明

《征服 C 指针》摘录4:函数 与 指针相关推荐

  1. 函数指针(指向函数的指针)

    目录 1. 函数指针(指向函数的指针) 1.1 使用函数指针(指向函数的指针) 1.2 重载函数的指针 1.3 函数指针形参(形参是指向函数的指针) 1.4 使用类型别名和 decltype 简化函数 ...

  2. Day17 指针变量做函数形参 指针函数 函数指针

    1. 指针变量做函数形参 1.1指针变量做一维数组形参 首先先了解如何定义一个一维数组指针 int arr[5] = {1, 2, 3, 4, 5};//定义一个一维数组 int *parr = ar ...

  3. 【C语言】函数指针(指向函数的指针)

    文章目录 1. 函数指针的定义 2. 函数指针的定义形式 2.1 一般定义方法 2.2 加上typedef关键字的定义 3.利用函数指针调用其所指向的函数 3.1 情况1:一般调用 3.2 情况2:函 ...

  4. 初论函数指针、指针函数、指针的指针

    一.指针函数 1.定义 指针函数是指带指针的函数,即本质是一个函数.函数返回类型是某一类型的指针 函数返回值类型 函数名(参数表) int * f(int x,int y); //函数返回值类型是in ...

  5. 【C 语言】指针 与 数组 ( 指针 | 数组 | 指针运算 | 数组访问方式 | 字符串 | 指针数组 | 数组指针 | 多维数组 | 多维指针 | 数组参数 | 函数指针 | 复杂指针解读)

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  6. (转)函数指针,指针函数,指向函数的指针,返回指针的函数

    摘要: 在学习C语言时,一个比较容易混淆的概念就是函数指针.指针函数.返回值为指针的函数.本文将对这三个概念进行区分.指针函数就是返回值为指针的函数,两个本质上是同一事物,只是叫法不同.函数指针就是一 ...

  7. 指针系统学习7-返回指针值的函数

    1.返回指针值的函数 一个函数可以带回一个整型值.字符值.实型值等,也可以带回指针型的数据,即地址.其概念与以前类似,只是带回的值的类型是指针类型而已. 这种带回指针值的函数,一般定义形式为      ...

  8. 数组指针和指针数组,函数指针和指针函数,常量指针和指针常量,常量引用

    一.数组指针和指针数组 1.数组指针(行指针) 首先要知道数组指针是指向数组的指针.所以数组指针本质是个指针,只不过指向一个数组而已.格式为:T (*ptr)[]. 注意:"[]" ...

  9. 【C/C++学习】之七、指向函数的指针

    什么是指向函数的指针 函数指针是指向函数的指针变量,不是指向对象的指针!函数指针本身应该是"指针变量": "在C语言中,函数本身不是变量,但可以定义指向函数的指针,这种指 ...

最新文章

  1. CoLoRMap: Correcting Long Reads by Mapping short reads CoLoRMap:通过映射短读来纠正长读
  2. Facade模式——设计模式学习笔记
  3. ISA CMAK 网络访问隔离区
  4. 分离图片中的隐藏文件方法总结
  5. 联想服务器网卡型号怎么看,如何通过设备硬件ID判断无线网卡的品牌及型号
  6. 这10道Java面试题!大部分的人回答不出来
  7. 关于多线程的一点感想
  8. Python Pillow(PIL)库的用法介绍(二)
  9. android的自定义字体,Android 自定义字体方案
  10. node.js学习笔记之浅谈观察者模式
  11. Java语言基础:IPO编程模式
  12. python游戏开发keydown_pygame.KEYDOWN移动对象
  13. emv交易流程介绍,简易波动指标EMV基础知识介绍:EMV的计算公式_EMV应用法则
  14. MAC 笔记本快捷键使用总结
  15. Error: Can't find Python executable python, you can set the PYTHON env variable.解决办法
  16. VS2013写的程序在Win XP下运行的完美解决方案(百分百成功)
  17. Ubuntu安装Lua
  18. PS Suite Studio Android 调试方法
  19. 天然和合成石墨的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  20. 模型驱动开发的幻象与现实

热门文章

  1. 计算机通信网络实验(三) RIP协议原理及配置
  2. 清华大学王玉计算机,计算机科学技术——人工智能.pdf
  3. CK-S650-PA60S半导体行业RFID读写器|读卡器N协议详细说明
  4. 仰望Cosmos星空的明与暗:全面解析ATOM 2.0
  5. python爬虫解析数据错误_Python网络爬虫数据解析的三种方式
  6. 通过Python利用ADSL服务器和tinyproxy构建数据自己的动态代理IP池,用django+redis做web服务,提供IP接口
  7. 气垫车-市场现状及未来发展趋势
  8. VB.net实现Singleton模式
  9. Linux下类迅雷的下载神器-uGet 2.0
  10. ElementUI入门