如何理解signal函数声明
Signal函数用起来其实很简单,但是回头看看他的声明,相信会有很多人表示费解。自己也在这个问题中纠结了好几年了,今天终于弄明白,很是兴奋,一起分享一下。
先看函数原型:void (*signal(int signo, void (*func)(int)))(int);对于看惯了类似unsigned int sleep(unsigned int seconds);这种声明的人们来说,signal的声明到底是个啥啊?signal是个函数,后面应该是形参啊,但为什么形参后面又来个形参,我们使用的时候可没有后面的(int)啊?
问题就出在这,难以理解的也是这里。我们又掉进了一个误区,我们往往以为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型的形参吗?这个问题我也纠结了一段时间。之后写了一段测试代码(最后给出),才总算弄明白。其实这样写更通用一些。
C语言在声明一个函数时,如果不指定形参,那么在定义时可以使用任意形参。而C++却不是这样,如果定义时的形参个数和声明时的对不上,那么就会报错。测试代码很好地说明了这样的一个差异,用gcc编译或者使用g++编译,两者的区别是显而易见的。
综上,C语言其实可以写的很精练,但过于精练往往带来的就是不好理解。作为一个热爱C语言的人来说,她的精练真是让人又爱又恨,如果有一天,我们能够完全理解,就能真正欣赏她的美了。
最后给出测试代码:
#include <stdio.h> typedef void (*func)(/*int*/); void print(int a)
{
printf("%d\n\n", a);
}
int main()
{
func f1 = print;
f1(); return ;
}
test
如何理解signal函数声明的更多相关文章
- 深入理解,函数声明、函数表达式、匿名函数、立即执行函数、window.onload的区别.
一.函数声明.函数表达式.匿名函数1.函数声明:function fnName () {…};使用function关键字声明一个函数,再指定一个函数名,叫函数声明. 2.函数表达式 var fnNam ...
- 理解函数声明--signal函数的声明
1.显示调用首地址为0的例程:(*(void(*)())0)() 显示调用首地址为0的例程的表达式为:(*(void(*)())0)() 分两步分析: 假定变量fp是一个函数指针,调用方法如下:(*f ...
- js函数声明外面使用小括号括起来再接一个小括号的写法
js函数声明外面使用小括号括起来再接一个小括号的写法 (function(){})(); (function(){}()); !function(){}(); 总结ps:意思将函数声明变成,直接执行的 ...
- 简单理解函数声明(以signal函数为例)
这两天遇到一些声明比较复杂的函数,比如signal函数,那我们先简单说说signal函数的用法:(参考<c陷阱与缺陷>) [signal:几乎所有c语言程序的实现过程中都要用到signal ...
- signal函数的原型声明void (*signal(int signo, void (*fun(int))))(int)分析
转:http://blog.sina.com.cn/s/blog_4850a7880100hnam.html void (*signal(int signo, void (*fun(int))))(i ...
- JavaScript 函数声明,函数表达式,匿名函数的区别,深入理解立即执行函数(function(){…})()
function fnName(){xxxx}; // 函数声明:使用function关键字声明一个函数,在指定一个函数名. //例如:(正常,因为 提升 了函数声明,函数调用可以在函数声明之前) f ...
- Linux 信号(二)—— signal 函数
弗洛伊德认为:要解决这些苦恼,当事人就要通过回忆并理解自己早期的童年经历,来获得对潜意识冲突的顿悟.弗洛伊德的疗法被称为“精神分析” (psychoanalysis),在 20 世纪的很长一段时间被心 ...
- Signal ()函数详细介绍 Linux函数
http://blog.csdn.net/ta893115871/article/details/7475095 Signal ()函数详细介绍 Linux函数 signal()函数理解 在<s ...
- Signal ()函数详细介绍 Linux函数(转)
Signal ()函数详细介绍 Linux函数 收藏人:紫火神兵 2012-09-27 | 阅:5659 转:22 | 来源 | 分享 signa ...
随机推荐
- div中显示某个网页
原文:div中显示某个网页 1.<iframe>方法 2.ajax方法 ajax+流实现无框架限制块刷新: 主框架index页面: js: $(function(){ $("#d ...
- CSS3字体模块
介绍 字体提供了包含字符的视觉表现的资源.在最简单的等级中,其包含由字符编码到表示这些字符的形状(被称为字形)的映射信息.根据一组标准字体属性被分入一个字体家族的字体共享一个通用设计风格.在一个家族中 ...
- 浅谈 js 字符串 search 方法
原文:浅谈 js 字符串 search 方法 这是一个很久以前的事情了,好像是安心兄弟在学习js的时候做的练习.具体记不清了,今天就来简单分析下 search 究竟是什么用的. 从字面意思理解,一个是 ...
- linux 线程回顾
额,时隔两年重新写博客了. 这次看一下thread_cond_wait(pthread_cond_t * cond, pthread_mutex_t *mutex)和thread_cond_signa ...
- C语言缓冲区清空
C语言中有几个基本输入函数: //获取字符系列 int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void); //获取行系列 ...
- [译]ava 设计模式之构造器
(文章翻译自Java Design Pattern: Builder) 构造器模式的关键之处在于它使用一步接招一步的流程去构建东西,例如:尽管构建的每一步是不相同的但是每一个产品还是遵循相同的流程. ...
- 通过localstorage和cookie实现记录文章的功能
我们在做页面的时候,会考虑记录用户曾经看过的文章的功能,并记录下来在页面中显示!但是在IE低版本的下是不支持localstorage的功能,只能采用cookie来代替本地存储的功能!实现的方法如下! ...
- NYOJ 58 步数最少 【BFS】
意甲冠军:不解释. 策略:如果: 这个问题也可以用深宽搜索搜索中使用.我曾经写过,使用深层搜索.最近的学校范围内的搜索,拿这个问题来试试你的手. 代码: #include<stdio.h> ...
- document对象属性documentMode与CompatMode
DOCTYPE DOCTYPE全称Document Type Declaration(文档类型声明,缩写DTD) DTD的声明影响浏览器对于CSS代码及Javascript脚本的解析. 渲染模式 渲染 ...
- status状态栏实现字符串走动
<script type="text/javascript" language="javascript"> var i = 0; var str=& ...