How to understand this define

匆匆过客 提交于 2019-12-04 00:56:46
John Bode

The general procedure: find the leftmost identifier and work your way out. Absent an explicit grouping with parentheses, postfix operators such as () and [] bind before unary operators like *; thus, the following are all true:

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

Applying these rules to the declaration, it breaks down as

       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

In short, signal returns a pointer to a function returning void. signal takes two parameters: an integer and a pointer to another function returning void.

You could use typedefs to make this easier to read (and the man page for signal on Ubuntu linux does just that); however, I think it's valuable to show the non-typedef'd version to demonstrate exactly how the syntax works. The typedef facility is wonderful, but you really need to understand how the underlying types work in order to use it effectively.

The signal function sets up a signal handler; the second argument is the function that is to be executed if a signal is received. A pointer to the current signal handler (if any) is returned.

For example, if you want your program to handle interrupt signals (such as from 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;
}

EDIT I extended the example code for signal to something that's hopefully more illustrative.

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

signal is function that takes int and a pointer to function taking int and returning void and returns a function pointer taking int and returning void. That is,

typedef void(*funcPtr)(int)

then we have

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

The syntax is indeed strange, and such things better be done with a typedef. As an example, if you want to declare a function that takes an int and returns a pointer to a function taking char and returning double will be

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

Edit: after a comment that reads "Wooooooow", I am providing another example which is more "woooow" :)

Let's declare a function that takes
1. a pointer to array of 5 pointers to functions each taking float and returning double.
2. a pointer to array of 3 ponters to arrays of 4 ints
and returns a pointer to function that takes a pointer to function taking int and returning a pointer to function taking float and returning void and returns unsigned int.

The typedef solution would be this:

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);

Now, the funny part :) Without typedefs this will be:

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

My god, did I just write that? :)

The Clockwise Spiral rule will help: http://c-faq.com/decl/spiral.anderson.html

There are three simple steps to follow:

Starting with the unknown element, move in a spiral/clockwise direction; when ecountering the following elements replace them with the corresponding english statements:

[X] or [] => Array X size of... or Array undefined size of...

(type1, type2) => function passing type1 and type2 returning...

  • => pointer(s) to...

Keep doing this in a spiral/clockwise direction until all tokens have been covered. Always resolve anything in parenthesis first!

See "Example #3: The 'Ultimate'", which is pretty much exactly what you are asking for:

"signal is a function passing an int and a pointer to a function passing an int returning nothing (void) returning a pointer to a function passing an int returning nothing (void)"

In case you don't have access to cdecl right now, here is the cdecl output:

$ 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

This site gives declerations to C gibberish:

C gibberish <-> English

Install cdecl for your distribution (if available) or go here

Otherwise, I believe Armen Tsirunyan's answer is correct.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!