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

aar*_*had 2 linux signals function-pointers

  void ( *signal(int signum, void (*handler)(int)) ) (int);  
Run Code Online (Sandbox Code Playgroud)

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

Win*_*ute 8

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

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
Run Code Online (Sandbox Code Playgroud)

在这里我们可以更清楚地看到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)
}
Run Code Online (Sandbox Code Playgroud)

阅读声明

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

C中的变量声明从内到外从右到左读取.当你的变量是a int或a时struct 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);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

这可以任意扩展:

// 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];
Run Code Online (Sandbox Code Playgroud)

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

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

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
Run Code Online (Sandbox Code Playgroud)

将其与上面的数组指针语法进行比较.这也可以任意扩展,函数指针可以出现在参数列表中(它们可以独立读取).所以,努力signal:一个功能

void foo(void (*fptr)(void));
Run Code Online (Sandbox Code Playgroud)

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

void (*foo(void))(void);
Run Code Online (Sandbox Code Playgroud)

让我们把它分开:

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

您可以看到,这也与我们在数组指针中看到的非常相似.现在,我们几乎回家了: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);
Run Code Online (Sandbox Code Playgroud)

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

但是因为我不能没有恐惧离开你:要知道你可以组合函数和数组指针.例如,

void (*foo[10])(void);
Run Code Online (Sandbox Code Playgroud)

是一个函数指针数组(事实上,它可以是有用的),和

int (*foo(void))[10];
Run Code Online (Sandbox Code Playgroud)

是一个返回指向数组的指针的函数.这样可以构建真正可怕的东西

int (*(*(*foo)[10])(void))[20];
Run Code Online (Sandbox Code Playgroud)

...这是一个指向函数返回指向数组指针的指针数组的指针.如果您确实需要此类的高阶类型,请使用typedef.这可以改写为

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

funcptr (*foo)[10];
Run Code Online (Sandbox Code Playgroud)

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