一、引言
每日学习~天天开心!
我们今天来研究两个有趣的代码:
(*(void(*)())0)();
void(* signal(int,void(*)(int)))(int);
这两句代码是《C缺陷与陷阱》一书中提到的,我们看到这两行代码是不是会觉得很复杂,觉得很不能理解,那么今天就让我们一起探讨一下吧~
二、(*(void(*)())0)();
我们一层一层的看:
首先,我们注意到了 void(*)() ,这个是一个参数为空,返回类型为void的函数指针类型。
紧接着,我们看到了一个0,我们会非常不解,为什么会有一个0,这就和当时提出这一行代码的那位前辈有关了,他想用0作为一个地址进行操作。所以这里是使用了强制类型转换:(void(*)())0,把0强制转换成一个函数地址(void(*)()),这样就可以进行使用操作了。
接下来的就很容易理解了,首先对这个地址进行了解引用操作,*(void(*)())0,这样我们通过地址找到了对应的函数。
最后,调用这个函数,(*(void(*)())0)();这样就实现了把0作为一个地址调用一个函数。
看到了这里,第一句代码,有没有觉得自己已经理解了呢。让我们往下看。
三、void(* signal(int,void(*)(int)))(int);
看到这句代码,只感觉头更疼了。没关系,我们慢慢来看。
和上面一样,首先,我们看到了 signal(int,void(*)(int)),signal是一个函数名,它有两个参数,分别为int类型和void(*)(int)类型,其中void(*)(int)是一个参数为int类型,返回值是void类型的函数指针类型。
但是,我们看到这里的时候就会发现,剩下的好像更复杂了,很奇怪的样子。
我们这样想,对于一个函数(返回类型 函数名(参数)),我们除掉函数名和参数,剩下的是不是就是返回类型,所以我们把 signal(int,void(*)(int))去掉,那么就会剩下void(* )(int)部分,我们会发现,这是一个函数指针。
所以,到这里,我们就可以得出结论了,这一个语句是一个函数的声明,这个函数名字为signal,参数为int和void(*)(int),返回类型是void(* )(int)。
四、关于void(* signal(int,void(*)(int)))(int);的思考
我们通常的函数声明是返回类型 函数名(参数);
所以我们可不可以写成void(* )(int) signal(int,void(* )(int));呢?我们输入编译器会发现,会出现错误,这个在语法上是不允许的。但是我们又觉得原式太复杂了,不容易阅读。
我们在这里,我们就想到了typedef关键词,我们可以使用它来进行重命名。
我们正常来使用,就是typedef 类型 新名字;例如:typedef unsigned int unit;
但是我们在这里不能写成:
typedef void(*)(int) pfun_t;
我们会发现编译器报错,我们要给一个函数指针重命名,我们要写成如下格式:
typedef void(*pfun_t)(int);
我们要把重命名的名字和*连在一起,我们可以这样理解,void(*pfun_t)(int)相当于定义了一个名字为pfun_t的函数指针,这样是不是更好理解呢?
综上所述,我们可以把它简化为:
typedef void(*pfun_t)(int);
pfun_t signal (int,pfun_t);
我们这样看着是不是也舒服了很多呢?
五、总结
今天的这两句代码,是不是也让大家对于函数指针有了一个更深的理解呢?今天的内容就到此结束啦!希望大家天天开心!