39

I came across this piece of code and completely got lost interpreting its meaning.

#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);

What is a detailed explanation for the code at line 2?

I know that void and int are types, the *func is a pointer for a function, and the brackets are for priority. But I still don't get the (*signal ...), the (int), and the whole thing combined together. The more detailed, the better.

Probably I've known the meaning/effect of this declaration. But I had to make some more trials to help me understand what's going on, as below:

  1 #include <signal.h>
  2 void (*signal)(int sig, void (*func)(int));
  3 void (*signal)(int);  // then void (signal)(int) again.
  4 //void (*signal(int sig, void (*func)(int)))(int); //break this line into two lines above
  5
  6 int main(){}

In the above code, I broke void (*signal(int sig, void (*func)(int)))(int) into two lines. For line 3, I tried both void (*signal)(int) and void (signal)(int), with the same error result that indicated that I was trying to redeclare signal:

TestDeclaration.c:2: error: 'signal' redeclared as different kind of symbol /usr/include/signal.h:93: error: previous declaration of 'signal' was here
TestDeclaration.c:3: error: 'signal' redeclared as different kind of symbol /usr/include/signal.h:93: error: previous declaration of 'signal' was here

Now I know both the trials are incorrect ways of declaration, but why are they incorrect? Why is the original way of declaration NOT a redeclaration?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
photon
  • 1,309
  • 2
  • 12
  • 13
  • 3
    +1 for showing that you actually understand *some* of it as opposed to *none* of it. – BoltClock Sep 14 '10 at 07:02
  • 3
    Try http://www.cdecl.org – Björn Pollex Sep 14 '10 at 07:13
  • 2
    Funnily, cdecl.org gives a syntax error on this one. Can someone explain this? – Björn Pollex Sep 14 '10 at 07:22
  • @Space Cowboy: It gives my syntax error for this one ... – Cedric H. Sep 14 '10 at 07:23
  • 3
    @Space_C0wb0y: It works if you remove the parameter names. – CB Bailey Sep 14 '10 at 07:25
  • how did this question get 12 votes? – Alexander Rafferty Sep 14 '10 at 07:27
  • @Alexander Rafferty because 12 (15 now) people judge it to be 'useful and clear'. Simple as that. – AakashM Sep 14 '10 at 09:05
  • @photon: Post-edit, you are now asking a different question, perhaps you should make it a different question? – CB Bailey Sep 14 '10 at 09:32
  • To answer your post-edit question. You can repeat the declaration of entities in C as often as you like but the types of the entities must match. `signal` is a standard function defined in `signal.h`. You can redeclare this function but your re-declaration must match the correct declaration. You can't declare it with a different type or define it yourself. – CB Bailey Sep 14 '10 at 09:34
  • possible duplicate of [Explain void (*signal(int signo, void *(func)(int)))(int)](http://stackoverflow.com/questions/2270713/explain-void-signalint-signo-void-funcintint) – sth Sep 16 '10 at 02:17

5 Answers5

45

It's the declaration of a function taking an int and a pointer to a function (taking int returning void) and returning a pointer to a function (taking int and returning void).


Explanation, or guide to interpretation

You can interpret by treating everything in parentheses as a single entity and then working inwards using the "declaration follows usage" rule.

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

The entity in the brackets looks like a function taking int and returning void.

Stripping away the outer part:

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

So, signal takes some parameters and returns something that can be dereferenced (due to the leading *) to form a function taking int and returning void.

This means signal is a function returning a pointer to a function (taking int and returning void).

Looking at the parameters it takes an int (i.e. sig) and void (*func)(int) which is a pointer to a function (taking int and returning void).

user3840170
  • 26,597
  • 4
  • 30
  • 62
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 1
    Personally, I'm not a fan of the clockwise/sprial rule. I prefer to work from the outside in. I know it's not a popular approach, but I'm much happier that I'm applying the grammar rules correctly with my approach. – CB Bailey Sep 14 '10 at 07:18
  • It's funny that the clockwise/spiral rule page linked above includes the very same declaration that the OP is asking about. – Jon Purdy Sep 15 '10 at 00:58
  • 1
    There's an even better answer in a duplicate of this question: http://stackoverflow.com/a/9501054/220060 – nalply Aug 11 '12 at 14:14
9

This is one of the classical examples of how convoluted C declarations can become.
To understand this declaration, it usually helps to introduce a typedef:

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

The typedef declares a pointer to a function (taking an int parameter and returning nothing). The function signal can now be seen as a function that takes two parameters (an int and a pointer to a function) and returns a pointer to a function.

This can also be derived from the original declaration, but it takes a bit of practice. The usual way is to start at the identifier that names the outermost entity (signal is this case):

signal is a ...

Then you read right until you find an unmatched closing parenthesis or the end of the declaration: void (*signal(int sig, void (*func)(int))(int)

signal is a function taking ... returning ...

Now you can choose between parsing the parameters first, or the return value first. I will do the return value first. For that, you read backwards to find the matching open parenthesis: void (signal( / ... */ ))(int)

signal is a function taking ... returning a pointer to ...

Reading back and forth this way you get at successive stages:

signal is a function taking ... returning a pointer to a (function taking ... returning ...)

signal is a function taking ... returning a pointer to a (function taking ... returning void)

signal is a function taking ... returning a pointer to a (function taking an int and returning void)

signal is a function taking two parameters: (an int) and (a pointer to a function taking an int and returning void), and returning a pointer to a (function taking an int and returning void)

user3840170
  • 26,597
  • 4
  • 30
  • 62
Bart van Ingen Schenau
  • 15,488
  • 4
  • 32
  • 41
3

A mnemonic I created many years ago, which is invaluable when trying to understand complicated types:

Remember these rules for C declares
And precedence never will be in doubt
Start with the Suffix, Proceed with the Prefix
And read both sets from the inside, out.

Except where parentheses change that precedence, of course.

Applying it to this case:

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

signal is:
  [inside parentheses]
  [suffix ()] a function, whose arguments are
    sig, which is [prefix int] an integer, and
      func, which is:
         [inside parentheses]
           [no suffix within these parens]
           [prefix *] a pointer to
         [suffix ()] a function, whose argument is
           an int
         [no more suffixes]
         [prefix void] and which returns void
         [no more prefixes]
       [no more arguments]
     [prefix *] And which returns a pointer to
     [no more prefixes within these parens]
   [suffix ()] a function, whose argument is
      an int
   [no more suffixes]
   [prefix void] and which returns void.

With a bit of practice, you'll get to the point where you can do all that on the fly:

"Signal is function, whose arguments are:
    sig, an integer,
    and func, a pointer to a function whose argument is an int and which returns void
... which returns a pointer to a function that takes int as an argument and returns void.

(Sorry about the error first time out -- I'm out of practice.)

Yes, that mnemonic (with the implied "except for parentheses, of course) works for all C declarations, no matter how badly the pointers, arrays, and functions are intermixed.

This is a REALLY useful skill to have when trying to figure out how someone else's code works... or even figuring out something of your own that you haven't seen in a long time.

But, yes, the better way to handle anything that you don't think people will be able to read at a glance is to build it up in layers with typedefs. The component types are likely to be useful themselves, and taking it one step at a time keeps people from getting lost trying to figure out which parenthesis matches which. Be kind to the next person who touches your code!

If you find the mnemonic useful, feel free to quote it elsewhere -- just give me credit as its author, please.

By the way, there are also "C Explainer" tools which will parse C delaractions and do the conversion to English description for you. Mine was called CEX, for obvious reasons, but many others exist and you should be able to find one if you don't want to commit this skill to wetware or if someone hands you something that's really too ugly for you to keep track of.

keshlam
  • 7,931
  • 2
  • 19
  • 33
  • And yes, the fact that Start and Suffix, and Proceed and Prefix, share initial consonants is deliberate; that helps you remember the correct order. – keshlam Jan 01 '14 at 18:28
3

Let's take an example of how this nasty declaration could be used:

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

Without too much verbosity, we could say that "signal" is a function with two parameters that returns a function.

#include <stdio.h>

// First function that could be returned by our principal function
// Note that we can point to it using void (*f)(int)
void car_is_red(int color)
{
    printf("[car_is_red] Color %d (red) is my favorite color too !\n", color);
}

// Second function that could be returned by our principal function
// Note that we can point to it using void (*f)(int)
void car_is_gray(int color)
{
    printf("[car_is_gray] I don't like the color %d (gray) either !\n", color);
}

// The function taken as second parameter by our principal function
// Note that we can point to it using void (*func)(int)
void show_car_type(int mod)
{
    printf("[show_car_type] Our car has the type: %d\n",mod);
}

/* Our principal function. Takes two parameters, returns a function. */
void (* show_car_attributes(int color, void (*func)(int)) )(int)
{
    printf("[show_car_attributes] Our car has the color: %d\n",color); // Use the first parameter

    int mod = 11;  // Some local variable of our function show_car_attributes()
    func(mod);  // Call the function pointed by the second parameter (i.e. show_car_type() )

    // Depending on color value, return the pointer to one of two functions
    // Note that we do NOT use braces with function names
    if (color == 1)
        return car_is_red;
    else
        return car_is_gray;
    }


//main() function
int main()
{
    int color = 2;   // Declare our color for the car
    void (*f)(int);  // Declare a pointer to a function with one parameter (int)

    f = show_car_attributes(color, show_car_type); // f will take the return 
           // value of our principal function. Stated without braces, the 
           // parameter  "show_car_types" is a function pointer of type 
           // void (*func)(int).

    f(color);  // Call function that was returned by show_car_attributes()

    return 0;
}

Let's see what will be output:

If color = 1

[show_car_attributes] Our car has the color: 1
[show_car_type] Our car has the type: 11
[car_is_red] Color 1 (red) is my favorite color too !

If color = 2

[show_car_attributes] Our car has the color: 2
[show_car_type] Our car has the type: 11
[car_is_gray] I don't like the color 2 (gray) either !
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
draganicimw
  • 87
  • 1
  • 1
0

Returning pointer to a function which takes an:

  • integer as first argument argument and
  • a pointer to a function (which takes an int and returns void) as an argument as a second argument.

And takes an integer argument.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vijay
  • 65,327
  • 90
  • 227
  • 319