signal的声明理解

1.显示调用首地址为0的例程:((void()())0)()

显示调用首地址为0的例程的表达式为:((void()())0)()

分两步分析:

假定变量fp是一个函数指针,调用方法如下:(fp)();
因为fp是一个函数指针,那么
fp就是该指针指向的函数,所以(fp)()就是调用该函数的方式。ANSI C标准允许程序员将上式简写为fp()。()的运算符高于

2.使用具体的函数指针替换fp,此处将0做类型强制转换,转换为函数指针,替换fp。

对一个常数进行类型转换,只需在变量声明中将变量名去掉即可。如果fp是一个指向返回值为void类型的函数的指针,那么(*fp)()的值为void,fp的声明如下:

void (fp)();将变量名去掉,将0转换为"指向返回值为void的函数的指针"类型这样写:(void()())0

因此,可以用(void(*)())0替换fp,从而得到:

*void(*)()0();

使用typedef声明更加便于理解。

typedef void(*funcptr)();     //相当于typedef void (*)()  funcptr

((*funcptr)0)();

2.signal函数声明理解:void(signal(int, void()(int)))(int)

从三个方面理解:返回值类型、函数指针以及参数。

signal函数的返回值类型是一个指向调用前的用户定义信号处理函数的指针,

假定特定信号处理函数的声明如下:

void sigfunc(int);

假定sfp为指向sigfunc的函数指针,则*sfp就代表了sigfunc函数,因此可以如下声明sfp:

void (*sfp)(int);

signal的参数列表是(int,sigfunc), 返回值是sigfunc,所以signal可以声明如下:

sigfunc signal(int, sigfunc)

替换sigfunc,即:void(*sfp)(int) signal(int, void(*sfp)(int))

去掉变量名,即:void (*)(int) signal(int, void(*)(int))

调顺序,即:void (*signal(int, void(*)(int)))(int)

使用typedef声明更加便于理解。

typedef void (*HANDLER) (int);

HANDLER signal (int, HANDLER);

问题就出在这,难以理解的也是这里。我们又掉进了一个误区,我们往往以为signal函数的返回值是void类似的,这样后面的声明部分就无法理解了。在网上找一些signal的使用方法:

if(signal(SIGINT,sig_int)==SIG_ERR)
err_sys(“can’t catch the SIGINT”);

SIG_ERR长得跟那些信号值好像,如果之前没看过signal的声明,那么急躁的人们就会想:signal函数返回的跟信号类型一样,是一个int型的值。因为往往判错的时候都只会用到SIG_ERR,记住这样用倒也不会产生太大的问题。

但这样也就太业余了,根本不利于成长啊。再看看SIG_ERR的定义,它可不是int型的:

#define SIG_ERR (void()())-1
#define SIG_DFL (void(
)())0
#define SIG_IGN (void(*)())1

然后就必须再弄明白signal函数了。其实声明最后的那个(int)是用来修饰返回值的,那什么类型的数据需要用形参来修饰呢?自然只有函数指针了。我们把这个声明修改一下:void (*p)(int);这是什么?它就是一个函数指针啊,该函数指针指向的函数有一个int型的参数。说到这,我们就很容易理解了,signal函数声明说明了signal函数的返回值是一个函数指针,该函数指向的函数有一个int型的参数。而signal(int signo, void (*func)(int));才是我们在函数里调用signal函数时的使用方法。

这里应当注意,signal函数返回的是一个函数指针,而不是一个指针函数。怎么理解呢?函数指针它是个指针,但该指针指向的是一个函数的入口,因此它需要指定参数类型。指针函数是指一个函数它的返回值是指针,普通类型的指针是不存在形参的概念的。将signal函数与char *ctime(const time_t *timep);进行对比。前者返回值是函数指针,因此最后有int来修饰形参,后者返回的是一个char型指针,是一个完整的数据类型,不需要任何修饰。

现在应该弄明白了,一个函数声明由函数返回类型、函数名和形参列表组成,signal函数就是函数返回类型复杂了一点。那能不能让它表现的更简单一些呢,最好就是像ctime那样,一眼就能让人看到返回值是char*?答案是肯定的。

Signal函数不是返回的是有一个int型参数、返回值是void型的函数指针吗,这样的函数指针的原型(ctime返回值的原型是char*)是什么呢?不是void,而是void(*)(int),这样的数据类型不好理解,我们可以给它换个简单的名字,取名字自然就是用typedef了:typedef void (*pSigfunc)(int);(注意这里有分号,typedef是编译时候处理的,突然发现似乎所有预处理命令后面都没有分号)当然,也可以给函数取名typedef void (Sigfunc)(int);对应修改后的signal函数声明可以简化为pSigfunc signal(int signo, pSigfunc func)或者Sigfunc* signal(int signo, Sigfunc* func);这样就和直观的ctime函数一样了吧!(此处需要对typedef有一定的了解,可以参考我转的一篇文章《typedef常见用法》)

最后看一下signal函数的那几个返回值。现在应该很容易理解,它们其实就是个强制类型转换,将一些默认错误码强制转换为一个函数指针。这个函数指针的原型就是void()()了。有人会问,()怎么没有函数名啊?答案也是很简单地,这是原型不是定义,只有定义时才会有变量。就像int a;int是原型,只有在定义变量时才会用到a(这个问题其实挺弱智的,但我自己一开始也没弄明白)。然后又有人会问,signal返回的那种函数指针不是有一个int型的形参吗?这个问题我也纠结了一段时间。之后写了一段测试代码(最后给出),才总算弄明白。其实这样写更通用一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值