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函数声明的更多相关文章

  1. 深入理解,函数声明、函数表达式、匿名函数、立即执行函数、window.onload的区别.

    一.函数声明.函数表达式.匿名函数1.函数声明:function fnName () {…};使用function关键字声明一个函数,再指定一个函数名,叫函数声明. 2.函数表达式 var fnNam ...

  2. 理解函数声明--signal函数的声明

    1.显示调用首地址为0的例程:(*(void(*)())0)() 显示调用首地址为0的例程的表达式为:(*(void(*)())0)() 分两步分析: 假定变量fp是一个函数指针,调用方法如下:(*f ...

  3. js函数声明外面使用小括号括起来再接一个小括号的写法

    js函数声明外面使用小括号括起来再接一个小括号的写法 (function(){})(); (function(){}()); !function(){}(); 总结ps:意思将函数声明变成,直接执行的 ...

  4. 简单理解函数声明(以signal函数为例)

    这两天遇到一些声明比较复杂的函数,比如signal函数,那我们先简单说说signal函数的用法:(参考<c陷阱与缺陷>) [signal:几乎所有c语言程序的实现过程中都要用到signal ...

  5. 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 ...

  6. JavaScript 函数声明,函数表达式,匿名函数的区别,深入理解立即执行函数(function(){…})()

    function fnName(){xxxx}; // 函数声明:使用function关键字声明一个函数,在指定一个函数名. //例如:(正常,因为 提升 了函数声明,函数调用可以在函数声明之前) f ...

  7. Linux 信号(二)—— signal 函数

    弗洛伊德认为:要解决这些苦恼,当事人就要通过回忆并理解自己早期的童年经历,来获得对潜意识冲突的顿悟.弗洛伊德的疗法被称为“精神分析” (psychoanalysis),在 20 世纪的很长一段时间被心 ...

  8. Signal ()函数详细介绍 Linux函数

    http://blog.csdn.net/ta893115871/article/details/7475095 Signal ()函数详细介绍 Linux函数 signal()函数理解 在<s ...

  9. Signal ()函数详细介绍 Linux函数(转)

    Signal ()函数详细介绍 Linux函数 收藏人:紫火神兵     2012-09-27 | 阅:5659  转:22    |   来源   |  分享               signa ...

随机推荐

  1. 取一种类型里面的产品销售前3甲的数据Sql

    需求:取出每种分类里面的销售前3甲的产品信息 表设计如下图: 数据如下: 两种方法可以实现: 1. SELECT * FROM (SELECT ROW_NUMBER() OVER(PARTITION ...

  2. Webserver管理系列:3、Windows Update

    微软的操作系统可以使用用户过程中发现了一些漏洞,因此,他们经常发布一些系统补丁.因此,我们需要自己主动安装更新功能后,打开系统. 默认的更新功能未开启自己主动: 开启自己主动更新功能后.Windows ...

  3. MVC EF 修改 封装类 通用泛型方法(二)

    修改 这个 方法 如下. 排除 null 值. /// <summary> /// 修改 多数 数据, 个别数据除外, proNames 不写 则是 修改全部 /// </summa ...

  4. AngularJs + ASP.NET MVC

    [AngularJs + ASP.NET MVC]使用AntularJs快速建立ASP.NET MVC SPA網站 這幾天接觸到了AngularJs的美麗,讓饅頭有點躍躍欲試使用AngularJs來做 ...

  5. JS子元素oumouseover触发父元素onmouseout

    原文:JS子元素oumouseover触发父元素onmouseout JavaScript中,父元素包含子元素: 当父级设置onmouseover及onmouseout时,鼠标从父级移入子级,则触发父 ...

  6. 如何运行代码apk安装

    import java.io.File; import android.app.Activity; import android.content.Intent; import android.net. ...

  7. lua及luci学习

    由于项目需要对Luci进行修改,所以这里开始地luci进行较深入的研究. 探索其中的运行路径. Openwrt默认的HTTP服务器为uhttpd,该WEB服务器是由Luci的开发者自行开发的,非常小巧 ...

  8. 六大利器助Java程序开发事半功倍

    实用的开发工具对于Java程序开发者来说,工作起来事半功倍.本文中小编将为大家列举包括开发环境.分析测试.代码保护等实用工具. 开发环境 Sonarqube Sonarqube是一个开源平台,是一款代 ...

  9. Linux Shell脚本入门--awk命令详解

    简单使用: awk :对于文件中一行行的独处来执行操作 . awk -F :'{print $1,$4}'   :使用‘:’来分割这一行,把这一行的第一第四个域打印出来 . 详细介绍: AWK命令介绍 ...

  10. WebIM(1)

    WebIM系列文章 之前笔者发布的云翔在线软件平台中已经包含了一个功能相对比较齐全的WebIM,这个系列的文章就是介绍如何开发出功能类似的WebIM,在文章开始前,先介绍一下相关的技术: 1.Come ...