《征服 C 指针》摘录4:函数 与 指针
一、指向函数的指针
函数名可以在表达式中被解读成“指向函数的指针”
,因此,正如代码清单 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 指针》摘录4:函数 与 指针的更多相关文章
- C语言函数指针(指向函数的指针)
转载:http://c.biancheng.net/cpp/html/3248.html 一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常 ...
- C:函数指针、回调函数
函数指针 是一个指针,指向函数的指针,指针存放的都是地址,所以函数指针存放的是函数的地址.数组名就是数组的首地址,函数名就是函数的首地址.与数组类似. 代码demo int (*p) (int ,in ...
- C语言函数指针变量和指针函数以及指针数组
C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址.我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数.然后通过指针变量就可以找到并调用这 ...
- 看懂c/c++ 函数、指针、数组定义
读懂 函数 + 指针 + 数组 c语言运算符机器优先级,看这里 结合运算符优先级,我们试着读懂函数和指针 优先级简单看 表达式提升():一级优先 函数():二级优先 数组[]:二级优先 指针定义*:三 ...
- C 函数指针与回调函数
函数指针是指向函数的指针变量. 通常我们说的指针变量是指向一个整型.字符型或数组等变量,而函数指针是指向函数. 函数指针可以像一般函数一样,用于调用函数.传递参数. 函数指针变量的声明: #inclu ...
- c指针 --笔记2返回指针值的函数
返回指针值的函数 一般带回指针值的函数,定义形式为: int *a (int x, int y); 看这个经典案例: #include <stdio.h> int main(int arg ...
- C语言中函数和指针的參数传递
近期写二叉树的数据结构实验.想用一个没有返回值的函数来创建一个树,发现这个树就是建立不起来,那么我就用这个样例讨论一下c语言中指针作为形參的函数中传递中隐藏的东西. 大家知道C++中有引用的概念,两个 ...
- C++(二十二) — 指针变量、函数指针、void指针
1.指针变量 (1)指针变量必须在初始化后才可以正常使用,初始化就是给他分配一个有效的数据地址. 先初始化,后使用. (2)指针可以进行加减运算,++ 或者 --:将指针的位置向前或者向后移动一个数据 ...
- C++指向函数的指针
直接上代码: #include<iostream> #include<string> #include<vector> using namespace std; t ...
随机推荐
- C++系统预定义4个用于标准数据流对象
C++系统预定义4个用于标准数据流对象 cin cout cerr clog C++里关于cerr,clog,cout三者的区别: cerr(无缓冲标准错误)-----没有缓冲,发送给它的 ...
- 关于webpack编译scss文件
css加载器文件通常和extract-text-webpack-plugin一块使用,我们可以在源文件src目录下写scss文件,然后通过webpack编译成css文件到输出目录public,这个目录 ...
- 【腾讯GAD暑期训练营游戏程序班】游戏中的设计模式作业说明文档
- SQL Server 常用关键字
SQL 建库 建表 --1.创建一个数据库 create database School; --删除数据库 drop database School; --创建数据库的时候指定一些选项. create ...
- JavaScript禁用回退
javascript:window.history.forward(1); 比如:当前在B页面,点击后退会回退到A页面,那么该代码写在A页面,然后在B页面就不会回退到A了.
- route使用详解
route命令用于显示和操作IP路由表.要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现.在Linux系统中,设置路由通常是 为了解决以下问题:该Linu ...
- Mbatis Oracle 第一次插入失败 useGeneratedKeys
<insert id="insertAgentInfo" parameterType="pd" useGeneratedKeys="false& ...
- redis 学习 01(下载 学习资源)
1. windows 版 redis 下载地址 https://github.com/MSOpenTech/redis/releases 2. redis 实战源码 http://redisinact ...
- Mysql 命令大全
1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root - ...
- 架构师养成记--4.volatile关键字
volatile修饰的变量可在多个线程间可见. 如下代码,在子线程运行期间主线程修改属性值并不对子线程产生影响,原因是子线程有自己独立的内存空间,其中有主内存中的变量副本. public class ...