一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

C语言中函数 与 指针学习笔记

时间:2022-06-25 04:43:01 编辑:袖梨 来源:一聚教程网

一、指向函数的指针

函数名可以在表达式中被解读成“指向函数的指针”,因此,正如代码清单 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, worldn");  // 无论如何,*就是什么也没做

三、复杂的声明

在 ANSI C 的标准库中,有一个 atexit()函数。如果使用这个函数,当程序正常结束的时候,可以回调一个指定的函数。
atexit()的原型定义如下:
int atexit(void (*func)(void));
1、首先着眼于标识符。
int atexit(void (*func)(void));
英语的表达为:
atexit is
2、解释用于函数的()。
int atexit(void (*func)(void));
英语的表达为:
atexit is function() returning
3、函数的参数部分比较复杂,所以先解析这部分。同样地, 先着眼于标识符。
int atexit(void (*func)(void));
英语的表达为:
atexit is function(func is) returning
4、因为有括号, 所以这里解释*。
int atexit(void (*func)(void));
英语的表达为:
atexit is function(func is pointer to) returning
5、解释用于函数的()。这里的参数还是比较简单的, 是 void(无参数) 。
int atexit(void (*func)(void));
英语的表达为:
atexit is function(func is pointer to function (void) returning) returning
6、解释类型指定符 void。这样就结束了 atexit 的参数部分的解释。
int atexit(void (*func)(void));
英语的表达为:
atexit is function(func is pointer to function(void) returning void) returning
7、解释数据类型修饰符 int。
int atexit(void (*func)(void));
英语的表达为:
atexit is function (func is pointer to function (void) returning void) returning int
8、翻译成中文……
atexit 是返回 int 的函数(参数是,指向返回 void 没有参数的函数的指针) 。
 
下面是一个更加复杂的例子。
标准库中有一个 signal()函数,它的原型声明如下,
void (*signal(int sig, void (*func)(int)))(int);
1、首先着眼于标识符。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is
2、相比*, ()的优先顺序更高,所以先解释这部分。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is function() returning
3、解释参数部分。这里有两个参数,第一参数是 int sig。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is function(sig is int,) returning
4、着眼另外一个参数。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is function(sig is int, func is) returning
5、因为有括号, 所以这里解释*。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is function(sig is int, func is pointer to) returning
6、解释表示函数的(), 参数为 int。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is function(sig is int, func is pointer to function(int) returning) returning
7、解释数据类型修饰符 void。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is function(sig is int, func is pointer to function(int) returning void) returning
8、参数部分已经解释结束。接着因为有括号,所以这里解释*。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is function(sig is int, func is pointer to function(int) returning void) returning pointer to
9、解释表示函数的(),参数为 int。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is function(sig is int, func is pointer to function(int) returning void) returning pointer to function(int) returning
10、最后,添上 void。
void (*signal(int sig, void (*func)(int)))(int);
英语的表达为:
signal is function(sig is int, func is pointer to function(int) returning void) returning pointer to function(int) returning void
11、翻译成中文……

signal 是返回“指向返回 void,参数为 int 的函数的指针”的函数,它有两个参数, 一个是 int,另一个是“指向返回 void 参数为 int 的函数的指针”。
如果能读懂这种难度的声明,我想应该不会再有什么让你畏惧的 C 声明了。
下面的说明可能会让你对 C 语言感到更加不快。
signal()是用于注册信号处理(当中断发生时被调用的函数)的函数。此函数的返回值是之前注册的处理当前信号中断的函数。
也就是说,其中的一个参数和返回值,它们都是相同的类型——指向信号处理函数的指针。在一般的语言中,同样的表现模式出现两次并不会让你感到不适,但是解释 C 语言声明的过程是“一会儿向左一会儿向右”,因此,表示返回值的部分散落了在左右两侧。

此时,运用 typedef 可以让声明变得格外得简洁。

/*摘录于FreeBSD 的man page /

typedef void(sig_t)(int);

sig_t signal(int sig, sig_t func);
sig_t 代表“指向信号处理函数的指针”这个类型。

热门栏目