返回函数指针的函数与signal函数原型分析
如果一个函数返回的是一个函数指针,那么这个函数的原型应该怎么写呢?
简单地说,有2种写法,借助typedef来写 和 不借助typedef来写。在具体分析之前,我们还是先来看一下关于函数指针的基本知识。
在讨论问题之前,先假设有以下这样一个普通函数,它有一个int型参数,返回值也是int型:
int func(int a)
{
return a+1;
}
然后,假设有一种函数指针,它所指向的就是如上的函数,即:有一个int型的参数且返回int型。
那么如何定义一个这种类型的函数指针的变量呢? 有2种方法。
定义函数指针变量的方法-1:
int (*pf)(int) = func;
当然,分两步也是一样的。
int (*pf)(int);
pf = func;
定义函数指针变量的方法-2:
typedef int (*FUNCPTR)(int);
FUNCTPR pf = func;
而通过函数指针来调用函数也有2种方法,简写的和不简写的,如下:
pf(10);
(*pf)(10);
现在回到最初的问题: 如果一个函数返回的就是一个函数指针,那么这个函数的原型怎么写?
假设这个函数返回的就是我们上面举例的那个函数指针,而这个函数没有任何参数。
首先,我们来看一下非常清晰的一种方式,即使用typedef.
返回函数指针的方法-1:
typedef int (*FUNCPTR)(int);
FUNCPTR f_ret_f(void);
所以,函数 f_ret_f 返回一个函数指针。(当然,上面的void不写也是一样)
然后,不用 typedef 的方法-2 应该是怎么样的呢? 很多人可能会想,这不是很简单么,就是像如下这样的吧。但其实,如下的这种直观的写法是错误的,不能通过gcc的编译。
// 一种错误的返回函数指针的函数原型
// 不能通过gcc编译
(int ((*)(int))) f_ret_f(void);
为什么以上不能通过gcc编译?(即使星号后面加一个变量名也不行。)
笔者也不知道为什么,毕竟,函数指针作为函数参数的时候是可以这么写的。
不过倒是有一种不直观的写法是可以通过编译的,即真正的方法-2.
返回函数指针的方法-2:
int (*f_ret_f(void))(int);
晕了吗?其实笔者也感觉不合逻辑,不过规律还是可以分析出来的。
先看里面的核心部分 f_ret_f(void)
这是什么呢? 这就是返回函数指针的那个函数的原型去掉了返回值的部分,即函数名加参数。
把这一部分设为x,代入原型,如下:
int (*x)(int);
代入之后的这个形式,恰恰就是返回的函数指针类型了。
所以,看起来 gcc 就是用这种方法来找出函数名、参数列表、返回值类型的。但这确实是不直观且似乎没有逻辑的一种方式。
相比之下,方法-1那是相当的清晰啊。平时编程中,肯定得推荐方法-1.
关于以上2种方法,完整的示例代码如下:
#include <stdio.h>
int func(int a)
{
printf(" func(): a = %d\n", a);
return a;
}
typedef int (*FUNCPTR)(int);
// Wrong semantics
// (int ((*)(int))) f_ret_f(void)
// {
// printf("f_ret_f(): \n");
// return func;
// }
int (*f_ret_f())(int)
{
printf("f_ret_f(): \n");
return func;
}
FUNCPTR f_ret_f_2(void)
{
printf("f_ret_f_2(): \n");
return func;
}
int main()
{
FUNCPTR pfa1 = f_ret_f();
pfa1(10);
int (*pfa2)(int);
pfa2 = f_ret_f();
pfa2(20);
FUNCPTR pfb1 = f_ret_f_2();
pfb1(30);
int (*pfb2)(int) = f_ret_f_2();
(*pfb2)(40);
return 0;
}
运行结果是:
f_ret_f():
func(): a = 10
f_ret_f():
func(): a = 20
f_ret_f_2():
func(): a = 30
f_ret_f_2():
func(): a = 40
说了这么多,现实中到底有没有函数会返回一个函数指针呢?
有。最经典的例子就是 signal 系统调用。
signal函数有2个参数:
- 第一个参数是int型,代表捕捉到的信号;
- 第二个参数是一个函数指针,它指向的函数有一个int型参数(也是代表信号)且没有返回值; 这个函数就是信号处理函数;
signal函数的返回值就是一个函数指针,指向的仍是上述的信号处理函数的类型,因为 signal 函数返回的是用户自定义信号处理函数之前的该信号的处理函数。
所以,signal函数的原型是怎样的呢?
先看 typedef 的形式(即方法-1):
typedef void (*HANDLER)(int);
HANDLER signal(int, HANDLER);
怎么样?这是相当的清晰啊:第二个参数和返回值都是指向信号处理函数的函数指针。
再看不用 typedef 的形式(即方法-2):
void (*signal(int, void (*)(int)))(int);
看明白了么? 其实有了之前的分析,这也不是很难了。
先看核心部分: signal(int, void (*)(int))
这就是 signal 函数名 加上 参数部分,而第二个参数是一个函数指针;
将这一部分设为x, 代入原型之后,其实就是返回值类型了: void (*x)(int);
(完)