I came across this piece of code and completely got lost interpreting its meaning.
#include
void (*signal(int sig, void (*func)(int)))(int);
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
).
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)
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.
Returning pointer to a function which takes an:
And takes an integer argument.
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 !