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语言的人来说,她的精练真是让人又爱又恨,如果有一天,我们能够完全理解,就能真正欣赏她的美了。

最后给出测试代码:

 1 #include <stdio.h>
 2
 3 typedef void (*func)(/*int*/);
 4
 5 void print(int a)
 6 {
 7     printf("%d\n\n", a);
 8 }
 9 int main()
10 {
11     func f1 = print;
12     f1(10);
13
14     return 1;
15 }

test

转载于:https://www.cnblogs.com/wxyy/p/3358880.html

如何理解signal函数声明相关推荐

  1. 理解signal函数

    关键字:Unix , Linux, signal, 函数指针,指针函数 在Unix/Linux中signal函数是比较复杂的一个,其定义原型如下: void (*signal(int signo,vo ...

  2. signal函数原型解读

    今天复习信号,被函数指针和signal函数的原型绕住了,看了几篇博客,这里记录一下. 以下内容摘自signal() 函数详解,想要理解signal的原型,要先理解signal函数的返回值是一个函数指针 ...

  3. c语言为什么提供函数声明机制,通过实战理解C语言精要——函数篇

    本文为作者原创,转载请注明出处,感谢您的阅读与分享,希望本文能让您有所收获. 前言 本篇博客是对C语言函数部分的重点内容和细枝末节通过实战得到的经验的总结精炼,不涵盖C语言函数的全部内容,所有提炼内容 ...

  4. C专家编程—分析signal函数的原型声明{void (*signal(int sig,void (*func)(int)))(int)}(2)

    在ANSI标准中,signal()的声明如下:      void (*signal(int sig,void (*func)(int)))(int) signal是一个函数,它返回一个函数指针,后者 ...

  5. 【C语言】函数的理解——关于函数的声明

    函数的理解--关于函数的声明 #include <stdio.h> //void sumx(int begin,int end);//函数的声明 void sumx(int ,int ) ...

  6. JS函数声明和预解析的理解

    JS函数声明方法 今天看到了一个自己关注了的大神给我回了私信,觉得自己仿佛摸到了大神的裤腿,哈哈,而且人还特别好,居然会给小菜鸟回私信,特别开心呀,一个菜鸟的小激动,言归正传啦 1.最为常见的函数声明 ...

  7. C中较为复杂的函数声明

    今天发现了一个较为好玩的函数声明: 是一个 signal的信号处理函数 是下面这个声明: **void (signal(int,void (func)(int)))(int); 对函数的声明和玩法 是 ...

  8. void func(int n)C语言,C语言高手帮忙分析一下复杂的函数声明

    c变量的声明有两部分组成:类型和声明符,对声明符求值得到结果类型为给定的类型. 举个最简单的例子 int a; a的类型就是上述声明中指定的类型,即int型. 同样,在来看指针和函数的声明,这里不考虑 ...

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

    signal()函数理解 在<signal.h> 这个头文件中. signal(参数1,参数2): 参数1:我们要进行处理的信号.系统的信号我们可以再终端键入 kill -l查看(共64个 ...

最新文章

  1. Docker创建Gitea(git服务)
  2. spring框架使用Quartz执行定时任务实例详解
  3. Scrum 项目1.0 2.0 3.0 4.0 5.0 6.0 7.0
  4. Struts1 处理接收参数插入到数据库之后呈乱码的问题解决
  5. 程序员最讨厌的100件事,瞬间笑喷了,哈哈~~
  6. 前端学习(2185):tabberitem传入active图片
  7. 【HDU - 3068】最长回文(Manacher算法,马拉车算法求最长回文子串)
  8. Linux的 i2c 驱动框架分析
  9. ajax获取表单php,Yii2基于Ajax自动获取表单数据的方法
  10. 循环矩阵的快速幂(bzoj 2510: 弱题)
  11. CNN中input,output的计算推导
  12. 接入TapTap防沉迷系统的逻辑图
  13. idea swagger生成接口文档_Springboot结合swagger-ui自动生成接口文档
  14. 350.两个数组的交集II
  15. 亿阳信通:不可表示的数
  16. 网络安全等级保护工作流程
  17. 多大士嘉堡校区的计算机科学,低分如何逆袭多伦多大学!
  18. java毕业设计_短视频分享网站
  19. MS-SQL Server 基础类 - SQL语句
  20. 怎么让计算机锁屏之后网络不断,win10系统如何设置锁屏时不断网|win10系统锁屏断网的还原办法...

热门文章

  1. VTK:可视化之DistanceToCamera
  2. VTK:Texture之TextureThreshold
  3. OpenGL HDR曝光的实例
  4. QT实现太阳系系统八大行星
  5. C++ Opengl 多重纹理源码
  6. STL常用的排序算法
  7. AttributeError: module ‘tensorflow‘ has no attribute ‘placeholder‘
  8. B02_NumPy数据属性(ndarray.ndim,ndarray.shape,ndarray.itemsize,ndarray.flags)
  9. HBase建表高级属性,hbase应用案例看行键设计,HBase和mapreduce结合,从Hbase中读取数据、分析,写入hdfs,从hdfs中读取数据写入Hbase,协处理器和二级索引
  10. 8.非关系型数据库(Nosql)之mongodb的应用场景(关系型数据库 和 Mongodb进行CRUD时数据用时的比较)