如何理解这个定义

12

最近我在阅读APUE,发现以下函数的定义:

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

我感到困惑,我知道信号是指向函数的指针,而最后的(int)是它的参数。 我不知道(int signo,void (*func)(int))是什么意思。


1
获取cdecl。它会告诉你所有这些信息。我问:explain void (*signal(int, void (*)(int)))(int);,它回答道:声明 signal 为函数 (int,指向返回 void 的函数指针 (int) 的指针) 的返回类型为指向返回 void 的函数指针 (int) 的指针 - Chris Lutz
6个回答

24
一般的步骤是:找到最左边的标识符,然后往外扩展。如果没有用括号进行分组,那么后缀运算符如()[]会在一元运算符如*之前绑定;因此,以下所有情况都是正确的:
T *x[N]             -- x is an N-element array of pointer to T
T (*x)[N]           -- x is a pointer to an N-element array of T
T *f()              -- f is a function returning a pointer to T
T (*f)()            -- f is a pointer to a function returning T

将这些规则应用于声明,它可以分解为:
       signal                                      -- signal
       signal(                            )        -- is a function
       signal(    signo,                  )        -- with a parameter named signo 
       signal(int signo,                  )        --   of type int
       signal(int signo,        func      )        -- and a parameter named func
       signal(int signo,       *func      )        --   of type pointer
       signal(int signo,      (*func)(   ))        --   to a function
       signal(int signo,      (*func)(int))        --   taking an int parameter
       signal(int signo, void (*func)(int))        --   and returning void
      *signal(int signo, void (*func)(int))        -- returning a pointer
     (*signal(int signo, void (*func)(int)))(   )  -- to a function
     (*signal(int signo, void (*func)(int)))(int)  -- taking an int parameter
void (*signal(int signo, void (*func)(int)))(int); -- and returning void

简而言之,signal 返回一个指向返回 void 的函数的指针。 signal 接受两个参数:一个整数和一个指向另一个返回 void 的函数的指针。
您可以使用 typedef 来使其更易读(Ubuntu Linux 上的 man 页面就是这样做的);然而,我认为展示非 typedef 版本很有价值,以便演示语法的确切工作方式。typedef 设施非常好,但您真的需要了解底层类型的工作原理才能有效地使用它。 signal 函数设置信号处理程序;第二个参数是在接收到信号时要执行的函数。返回当前信号处理程序(如果有)。例如,如果你想让你的程序处理中断信号(如来自 Ctrl-C 的信号):
static int g_interruptFlag = 0;

void interruptHandler(int sig)
{
  g_interruptFlag = 1;
}

int main(void)
{
  ...
  /**
   * Install the interrupt handler, saving the previous interrupt handler
   */
  void (*oldInterruptHandler)(int) = signal(SIGINT, interruptHandler);

  while (!g_interruptFlag)
  {
    // do something interesting until someone hits Ctrl-C
  }

  /**
   * Restore the previous interrupt handler (not necessary for this particular
   * example, but there may be cases where you want to swap out signal handlers
   * after handling a specific condition)
   */
  signal(SIGINT, oldInterruptHandler);
  return 0;
}

编辑 我扩展了signal的示例代码,希望更具有说明性。


16
void (*signal(int signo, void (*func)(int)))(int);

signal是一个函数,它接受一个整数和一个指向接受整数并返回void的函数的指针,并返回一个接受整数并返回void的函数指针。

typedef void(*funcPtr)(int)

那么我们就有

funcPtr signal(int signo, funcPtr func); //equivalent to the above

语法确实很奇怪,最好使用typedef来完成这样的操作。例如,如果你想声明一个函数,该函数接受一个int并返回一个指向带有char参数并返回double的函数的指针,那么将是:

double (*f(int))(char);

编辑:在一条评论中,读到了“哇哇哇”的留言后,我提供了另一个更加“哇哇”的示例:)

让我们声明一个函数,它需要:
1. 一个指向包含5个指向接受浮点数并返回双精度值的函数指针的数组的指针。
2. 一个指向包含3个指向4个整数数组的指针的数组的指针。
并且这个函数返回一个指向接受一个指向接受整数并返回一个指向接受浮点数并返回空指针的函数的指针,并返回无符号整数的函数指针。

typedef 解决方案如下:

typedef double (*f1ptr) (float);
typedef f1ptr (*arr1ptr)[5];
typedef int (*arr2ptr)[4];
typedef arr2ptr (*arr3ptr)[3];
typedef void(*f2Ptr)(float);
typedef f2ptr (*f3ptr)(int);
typedef unsigned int (*f4ptr) (f3ptr);
f4ptr TheFunction(arr1ptr arg1, arr3ptr arg2);

现在到了有趣的部分 :) 如果没有typedef,这将是:

 unsigned int (*TheFunction( double (*(*)[5])(float), int(*(*)[3])[4]))( void(*(*)(int))(float))

我的天,我刚刚写下了那个? :)


有趣吗?非typedef版本是完全透明的。 - John Bode
@John: 如果我有轮子,我就会成为一辆马车 :) - Armen Tsirunyan

12
Clockwise Spiral规则将有所帮助: http://c-faq.com/decl/spiral.anderson.html 有三个简单的步骤要遵循:
从未知元素开始,向螺旋/顺时针方向移动;遇到以下元素时,请用相应的英语语句替换它们:
[X] 或 [] => 数组X大小为... 或 数组大小未定义...
(type1, type2) => 函数传递type1和type2并返回...
=> 指向指针的...
继续以螺旋/顺时针方向执行此操作,直到涵盖所有令牌。 始终先解决括号中的任何内容!
请参见“示例#3:'Ultimate'”,这几乎就是您要求的内容:
“signal是一个函数,传递int和指向传递int且返回void的函数指针,再返回指向传递int且返回void的函数指针”。

这是一个非常好的资源。谢谢! - Shrayas

3

如果您现在无法访问cdecl,这里是输出结果:

$ cdecl
cdecl> explain void (*signal(int , void (*)(int)))(int);
declare signal as function (int, pointer to function (int) returning void) returning pointer to function (int) returning void

1

0

为您的发行版安装cdecl(如果有的话),或者前往此处进行安装。

否则,我相信Armen Tsirunyan的答案是正确的。


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