理解函数指针(信号系统调用)

时间:2014-12-14 10:34:24

标签: linux signals function-pointers

  void ( *signal(int signum, void (*handler)(int)) ) (int);  

我有一个问题,就是要了解信号如何获取输入以及返回的内容 给我你宝贵的解释 1.这个功能指针实际上有效吗? 2.我们需要了解吗?

1 个答案:

答案 0 :(得分:8)

或许首先要说清楚我们正在努力做些什么。从signal的手册页窃取:

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

这里我们可以更清楚地看到signal是一个接受两个参数的函数 - 一个int和一个函数指针 - 并返回一个函数指针。

函数指针

函数指针的工作方式非常简单:使用函数指针,使其指向函数,然后可以通过指针调用函数。例如:

#include <stdio.h>

void hello(int x) { 
  printf("Hello, world! x is %d.\n", x);
}

int main(void) {
  void (*ptr)(int) = hello;
  ptr(2); // calls hello(2)
}

阅读声明

信号的签名看起来很可怕,很大程度上是因为变量声明语法的一半规则是你只需要指向函数和数组的指针,即便这样也很少被过度使用。我会尝试揭开它的神秘面纱:

C中的变量声明从由内向外从右向左读取。当变量是intstruct foo时,这没有区别,但它对于指针,数组和函数很重要。 从右到左部分相当简单:

// a is an array ----+
// of pointers --+   |
// to int ----v  v   v
             int *aa[10];

// f is a function ----+
// returning ptr -+    |
// to int -----v  v    v
              int *foo(void);

由内到外位是我认为只能看到函数和数组指针的位。这是因为它有点心态,因此将其排除在更常见的用例之外是有意义的。指针数组比指向数组的指针更常见,返回指针的函数比函数指针更常见,所以谢天谢地,你不需要它。但是,如果我们希望能够使用它们,我们需要一些方法来声明这些数据类型。

那么语法如何容纳指向函数的指针,以及指向指针数组的指针等组合呢?

让我们开始使用较少使用的数组指针,因为语法不那么拥挤:

int arr[10];        // is an array
int *ptrarr[10];    // is an array of pointers
int (*arrptr)[10];  // is a pointer to an array.
int *(*arrptr)[10]; // is a pointer to an array of pointers

括号提供了一个嵌套结构,我们从每个级别的从里到外从右到左读取:

// arrptr is a pointer -+
// to an array ---------|-------+
// of pointers -------+ |       |
// to int ---------v  v v       v
                  int *(*arrptr)[10]; // is a pointer to an array.

这可以任意扩展:

// foo is an array --------+
// of pointers -------+    |
// to array ----------|----|----+
// of pointers -----+ |    |    |
// of pointers ---+ | |    |    |
// to array ------|-|-|----|----|---+
// of pointers -+ | | |    |    |   |
// to int ---v  v v v v    v    v   v
            int *(* *(*foo[10])[20])[30];

虽然:请不要做那样的事情。

然后,函数指针与数组指针的工作方式大致相同,只是数组边界被参数列表替换。我们有

void f(void);          // a function taking no arguments and returning nothing
void *ptrf(void)       // a function taking no arguments and returning a pointer
void (*fptr)(void);    // a pointer to a function taking no arguments and returning nothing
void *(*ptrfptr)(void) // a pointer to a function taking no arguments and returning a pointer

将此与上面的数组指针语法进行比较。这也可以任意扩展,函数指针可以出现在参数列表中(它们可以独立读取)。因此,最多可以使用signal:一个函数

void foo(void (*fptr)(void));

是一个接受函数指针作为参数的函数。除了参数列表中可怕的函数指针之外,这非常类似于您习惯的函数声明。可怕的部分是一个返回函数指针的函数,它看起来像这样:

void (*foo(void))(void);

让我们把它分开:

// foo is a function -----------------------
// returning a pointer ----------------+   |
// to a function taking no arguments --|---|-------+
// returning nothing -------------v    v   v       v
                                 void (*foo(void))(void);

你可以看到,这也与我们在数组指针中看到的非常相似。现在,我们几乎回家:signal非常有用,只有参数列表不同。再看一遍:

// signal is a function ------------+
// returning a pointer ------+      |
// to a function taking int -|------|-------------------------------------+
// returning nothing --v     v      v                                     v
                      void ( *signal(int signum, void (*handler)(int)) ) (int);

signal本身的参数列表 - int signum, void (*handler)(int)) - 可以单独阅读,并且它应该不再为你带来恐怖,现在你可以看到哪个参数列表属于什么。

但是因为我不能没有恐怖地离开你:要注意你可以组合函数和数组指针。例如,

void (*foo[10])(void);

是一个函数指针数组(实际上可能很有用)和

int (*foo(void))[10];

是一个返回指向数组的指针的函数。这允许构建真正可怕的事情,如

int (*(*(*foo)[10])(void))[20];

...这就是,让我们看一下指向函数的指针,该指针用于返回指向数组的指针。如果您确实需要此类高阶类型,请使用typedef 。这可以改写为

typedef int (*arrptr)[20];
typedef arrptr (*funcptr)(void);

funcptr (*foo)[10];

将其与原始声明进行比较,并告诉我哪个更有意义。