返回函数指针的函数与signal函数原型分析

返回函数指针的函数与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);

(完)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值