如何理解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 ...
随机推荐
- HDOJ 3518 Boring counting
SAM基本操作 拓扑寻求每个节点 最左边的出现left,最右边的出现right,已经有几个num ...... 对于每个出现两次以上的节点.对其所相应的一串子串的长度范围 [fa->len+1 ...
- 清理收缩VMware虚拟机MacOS系统的vmdk文件大小
屌丝行和差的主要标准,尽管持续性眼贪婪mbp.但是,从另一方面限制患有米,只是在虚拟机中播放MacOS.(我不会告诉你我的笔记本i5+120SSD+500HHD+12G内存,跑MacOS虚拟机一点不卡 ...
- 基于Hama并联平台Finding a Maximal Independent Set 设计与实现算法
笔者:白松 NPU学生. 转载请注明出处:http://blog.csdn.net/xin_jmail/article/details/32101483. 本文參加了2014年CSDN博文大赛,假设您 ...
- iOS7 UIKit动力学-碰撞特性UICollisionBehavior 上
我们谈到了重力上述财产UIGravityBehavior这个类. 非常明确的看法,当我们添加的属性的严重性后,,苹果UIview像掉进无底洞,地下坠,不断的加速.而如今呢,我们要在这个手机屏幕上,加入 ...
- 【Java&Android开源库代码分析】のandroid-async-http の开盘
在<[Java&Android开源库代码剖析]のandroid-smart-image-view>一文中我们提到了android-async-http这个开源库,本文正 ...
- JavaScript中的作用域和声明提前
[翻译]JavaScript中的作用域和声明提前 原文:http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html ===翻译 ...
- UpdateModel方法
WebForm 对 MVC 说:能否借你的UpdateModel方法来用用? 背景 ASP.NET MVC的Controller有个很不错的方法:UpdataModel (相对应的还有TryUpdat ...
- .NET的DTO映射工具AutoMapper
.NET的DTO映射工具AutoMapper 原文:https://github.com/AutoMapper/AutoMapper/wiki/Getting-started 参考:http://ww ...
- HTTP2协议之HPACK--之头部压缩规范介绍
接下来打算把HTTP2协议的头部压缩算法给翻译下,敬请等候. HPACK - Header Compression for HTTP/2 HPACK:HTTP/2头部压缩 概要说明 这个规范定义了HP ...
- leetcode第35题--Valid Sudoku
题目:Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudoku board could ...