一、指向函数的指针

函数名可以在表达式中被解读成“指向函数的指针”,因此,正如代码清单 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. C语言函数指针(指向函数的指针)

    转载:http://c.biancheng.net/cpp/html/3248.html 一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常 ...

  2. C:函数指针、回调函数

    函数指针 是一个指针,指向函数的指针,指针存放的都是地址,所以函数指针存放的是函数的地址.数组名就是数组的首地址,函数名就是函数的首地址.与数组类似. 代码demo int (*p) (int ,in ...

  3. C语言函数指针变量和指针函数以及指针数组

    C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址.我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数.然后通过指针变量就可以找到并调用这 ...

  4. 看懂c/c++ 函数、指针、数组定义

    读懂 函数 + 指针 + 数组 c语言运算符机器优先级,看这里 结合运算符优先级,我们试着读懂函数和指针 优先级简单看 表达式提升():一级优先 函数():二级优先 数组[]:二级优先 指针定义*:三 ...

  5. C 函数指针与回调函数

    函数指针是指向函数的指针变量. 通常我们说的指针变量是指向一个整型.字符型或数组等变量,而函数指针是指向函数. 函数指针可以像一般函数一样,用于调用函数.传递参数. 函数指针变量的声明: #inclu ...

  6. c指针 --笔记2返回指针值的函数

    返回指针值的函数 一般带回指针值的函数,定义形式为: int *a (int x, int y); 看这个经典案例: #include <stdio.h> int main(int arg ...

  7. C语言中函数和指针的參数传递

    近期写二叉树的数据结构实验.想用一个没有返回值的函数来创建一个树,发现这个树就是建立不起来,那么我就用这个样例讨论一下c语言中指针作为形參的函数中传递中隐藏的东西. 大家知道C++中有引用的概念,两个 ...

  8. C++(二十二) — 指针变量、函数指针、void指针

    1.指针变量 (1)指针变量必须在初始化后才可以正常使用,初始化就是给他分配一个有效的数据地址. 先初始化,后使用. (2)指针可以进行加减运算,++ 或者 --:将指针的位置向前或者向后移动一个数据 ...

  9. C++指向函数的指针

    直接上代码: #include<iostream> #include<string> #include<vector> using namespace std; t ...

随机推荐

  1. c++学习之容器细枝末节(2)

    从昨天到现在,还依然停留在容器的学习上,现在写例程代码顺手多了,看来写代码还是要多多练习才能有感觉. 经过一天的学习,有一下几点知识点让我觉得很有意义: (1)删除容器中的元素的时候,pop_fron ...

  2. Java面向对象 第一章 面向对象开发方法概述

    一.软件开发经历的生命周期: ①软件分析 ②软件设计 ③软件编码 ④ 软件测试 ⑤ 软件部署 ⑥软件维护 二.为了提高软件开发效率,降低软件开发成本,一个优良的软件系统应该具备以下特点: ① 可重用性 ...

  3. python基础之循环结构以及列表

    python基础之编译器选择,循环结构,列表 本节内容 python IDE的选择 字符串的格式化输出 数据类型 循环结构 列表 简单购物车的编写 1.python IDE的选择 IDE的全称叫做集成 ...

  4. concat() 方法用于连接两个或多个数组。

    我们创建了三个数组,然后使用 concat() 把它们连接起来: <script type="text/javascript"> var arr = new Array ...

  5. ffmpeg.exe dos下怎么用 放在哪里

    系统:windows 7 1.先看dos界面,win7下这里输入cmd, 看路径 2.把下载的ffmpeg.exe复制到这个路径下  3.这就可以用命令了 1.mp4说明这个文件是跟ffmpeg.ex ...

  6. Eclipse JEE 安装JBPM 4.4 GPD

    回顾往昔,发现自己好久没写博客了,想想以前自己是多么热衷于写博客分享,虽然分享的都是比较基础的东西,但每每看到访问量不断增加的时候内心还是爽爽的. 时间过的真的很快,离开学校和团队已经一个月了.来到了 ...

  7. 简单所以不要忽视,关于\r\n和\n程序员应了解的实际应用

    众所周知,\r叫回车符,\n叫换行符. 由于历史原因,windows环境下的换行符是\r\n;(文章最后会稍微解释这个历史原因) linux和html等开源或公开标准中的换行符是\n. 记录这篇笔记的 ...

  8. jQuery之XML的加载和解析

    1.XML(eXtensible Markup Language)即可扩展标记语言,与HTML一样,都是SGML标准通用语言.语法如下: 任何起始标签都必须有一个结束标签. 可以采用另一种简化语法,即 ...

  9. javascript中怎么判断对象{}为空

    有时候通过AJAX方法调用返回的是一个JSON对象,而这个对象可能在开发过程中会没有数据是一个空{}. JavaScript判断object/json 是否为空,可以使用jQuery的isEmptyO ...

  10. bzoj 3055礼物运送 floyed + 状压DP

    bzoj 3055: 礼物运送 floyed first 设f[i][S]表示取到了S集合中的所有点(不一定是经过的所有点),最后停在了i的最优值. 初始就f[i][{i}] = dis[1][i] ...