如何在C语言中阅读这个复杂的声明?

17

可能是重复的问题:
这段代码的含义是什么? void (*signal(int sig, void (*func)(int)))(int);

我有一个复杂的声明,它来自于 "signal.h" 头文件,以下是该声明。

  void (*signal(int sig, void (*func)(int)))(int);

现在,我该如何解析它?

signal是一个接受两个参数的函数,第一个参数‘sig’为int型,第二个参数‘func’是一个指向接受int类型参数并返回void类型的函数的指针;它返回一个指向接受int类型参数并返回void类型的函数的指针。

这样写可以吗?或者signal是一个指向函数的指针吗?


但这就是我感到困惑的地方,来源于http://www.joyofprogramming.com/Docs_ColumnArticles/36-JoP-Dec-09.pdf。 - Amit Singh Tomar
typedef int foo(void):foo是一个函数指针,但你可以简写为函数,因为你可以这样做 foo x; x(); - Benoit
@Benoit,您能否将其添加为您的答案? - Amit Singh Tomar
我不确定我的问题被关闭的依据是什么,因为我所问的与之前所问的不同。 - Amit Singh Tomar
5个回答

63

从最左边的标识符开始,逐步向外扩展。要记住[]()的优先级高于*,因此*a[]是指针数组,(*a)[]是指向数组的指针,*f()是返回指针的函数,(*f)()是指向函数的指针:

       signal                                     -- signal
       signal(                          )         -- is a function
       signal(    sig,                  )         -- with a parameter named sig
       signal(int sig,                  )         --   of type int
       signal(int sig,        func      )         -- and a parameter named func
       signal(int sig,      (*func)     )         --   which is a pointer
       signal(int sig,      (*func)(   ))         --   to a function
       signal(int sig,      (*func)(int))         --     taking an int parameter
       signal(int sig, void (*func)(int))         --     and returning void
      *signal(int sig, void (*func)(int))         -- returning a pointer
     (*signal(int sig, void (*func)(int)))(   )   -- to a function
     (*signal(int sig, void (*func)(int)))(int)   --   taking an int parameter
void (*signal(int sig, void (*func)(int)))(int);  --   and returning void

signal函数将信号处理函数func与信号sig相关联,并返回旧的信号处理程序函数指针:

void new_interrupt_handler(int sig)
{
  ... // do something interesting with interrupt signal
}

int main(void)
{
  void (*old_interrupt_handler)(int);
  ...
  /**
   * Set up our new interrupt handler
   */
  old_interrupt_handler = signal(SIGINT, new_interrupt_handler);
  ...
  /**
   * Restore original interrupt handler
   */
  signal(SIGINT, old_interrupt_handler);
  ...
}

4
非常好,逐步解释得很清晰。 - watbywbarif
感谢@John的想法,但这里有些用户反对并表示信号是指向函数的指针。 - Amit Singh Tomar
@Amit - 在Aaron的情况下,他提供给cdecl的字符串并不是你在问题中写的。 - John Bode
1
+1 使用typedef定义函数指针的原因之一是:它更容易理解。 :) - netcoder
1
@netcoder:这样做确实可以使声明更易于阅读,但我喜欢知道函数签名的样子,而不必搜索typedef。 sighandler func; 不告诉我如何使用 func,而 void (*func)(int); 则有所说明。 - John Bode
+1 我希望你的答案可以发布在原先发布的那个问题上,而这个问题是一个重复的。 - Joshua Drake

3

使用cdecl.org,您可以得到以下结果:

声明signal为函数(int,指向返回void的函数指针)返回指向返回void的函数指针的函数

输入为:

void (*signal(int, void(*)(int)))(int)

这意味着signal是一个函数。调用signal的结果是指向函数void f(int)的指针。
解释:调用signal()会安装一个新的信号处理程序,并返回旧的信号处理程序(这样您可以稍后恢复它,如果需要)。

但在我的情境中,它是不同的,不是吗? - Amit Singh Tomar
@Aaron - 你没有输入与 OP 相同的声明。 - John Bode
@JohnBode:你说得对。已修复。 - Aaron Digulla
+1 链接到cdecl.org - J. C. Salomon

2
void (*signal(int, void (*)(int)))(int);  

       signal(                  )         // signal is a function
              int, void (*)(int)          // the parameter types of the function:
                                          //    an int and a function pointer (take int, return void)
void (*                          )(int);  // the return type of the function:
                                          //    a function pointer (take int, return void)

// 编辑,参考John的答案。


现在指针如何成为函数的信号? - Amit Singh Tomar
它表示信号是返回指针的函数。 - Amit Singh Tomar
现在真的很令人困惑,大多数答案都说它是返回指针的函数,而你却说不同。 - Amit Singh Tomar
就像我在我的回答中所说的那样,()*的优先级更高,因此*f()是返回指针的函数,而不是指向函数的指针。signal的声明形式包括*signal(...),因此signal肯定是返回指针的函数。 - John Bode
尽可能简洁明了,不必追求完美的语法,这样对大脑的负担会小得多。 - grenix

1

没错。signal函数接受两个参数,一个整数和一个指向函数的指针,并返回一个指向函数的指针(与func参数具有相同的签名)。

这类似于(在我看来)更易读的方式:

typedef void (*sig_func)(int);
sig_func signal(int sig, sig_func func);

GNU称其为sighandler_t,libc4和libc5称其为SignalHandler,glibc称其为sig_t,顺便说一句。;) - Gandaro
这是你应该编写代码的方式。如果在声明某些模糊的东西,比如“将函数指针作为参数传递的函数指针”,你不使用typedef,则你是邪恶的,可能非常愚蠢。 - Lundin

1

signal 是一个函数,它接受两个参数并返回一个指向函数的指针,该函数接受一个 int 作为参数并返回 void

signal 接受的两个参数是一个 int 和一个指向函数的指针,该函数接受 int 作为参数并返回 void

是的,你对描述和整体思路都理解正确。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接