There are some techniques to reading and understanding complex declarations. There's a trick known as the "clockwise spiral rule", where you start with the identifier and spiral outward starting with whatever's immediately to the right. For example, given the declaration
int *(*a[N])(void);
we'd trace it as:
+-----------------------+
| +-----------+ |
| | +-------+ | |
| | | +-+ | | |
| | | | | | | |
int * ( * a [N] ) ( void ) |
^ | | | | | | |
| | | +---+ | | |
| | +---------+ | |
| +-------------+ |
+---------------------------+
or
a -- a
a[N] -- is an N-element array
*a[N] -- of pointers
(*a[N])( ) -- to functions taking
(*a[N])(void) -- no parameters
*(*a[N])(void) -- returning pointer to
int *(*a[N])(void); -- int
Remember that the postfix subscript []
and function call ()
operators have higher precedence than the unary dereference *
operator, so expressions like *a[i]
and *f()
are parsed as *(a[i])
and *(f())
- we're dereferencing the results of a[i]
and f()
. If a
points to an array, then we need to index into the result of *a
, so we need to use parentheses to explicitly group *
with a
- (*a)[i]
. Similarly, if f
points to a function, then we need to call the result of *f
, so we have to write (*f)()
.
Here's an example with multiple indirection:
int **foo(void);
which breaks down as
foo -- foo
foo( ) -- is a function taking
foo(void) -- no parameters
*foo(void) -- returning a pointer
**foo(void) -- to a pointer
int **foo(void); -- to int
You'd recursively apply these rules to any function parameters. Here's the declaration of the signal
function from the standard library:
void (*signal(int sig, void (*func)(int)))(int);
which reads as
signal -- signal
signal( ) -- is a function taking
signal( sig ) -- parameter sig
signal(int sig ) -- is an int
signal(int sig, func ) -- parameter func
signal(int sig, *func ) -- is a pointer
signal(int sig, (*func)( )) -- to a function taking
signal(int sig, (*func)(int)) -- parameter unnamed is an int
signal(int sig, void (*func)(int)) -- returning void
(*signal(int sig, void (*func)(int))) -- returning a pointer
(*signal(int sig, void (*func)(int)))( ) -- to a function taking
(*signal(int sig, void (*func)(int)))(int) -- unnamed parameter is an int
void (*signal(int sig, void (*func)(int)))(int); -- returning void
There are also some substitution tricks you can play with. If you need to figure out how to declare an array of pointers to functions returning pointers to other functions returning pointers to arrays of pointers to int
, start with an array of T
:
T a[N]; // a is an array of T
This is going to be an array of pointers to something, so replace T
with a pointer type P
:
P *a[N]; // a is an array of pointer to P
Each a[i]
is going to be a pointer to a function, so replace P
with the function type F
:
F (*a[N])( );
Each of these functions returns a pointer, so replace F
with another pointer type P
:
P *(*a[N])( );
Each of these pointers points to another function, so
F (*(*a[N])( ))( );
Those functions return pointers, so we replace F
with another pointer type P
:
P *(*(*a[N])( ))( );
Replace P
with an array type A
:
A (*(*(*a[N])( ))( ))[M];
And from here we can skip straight to the end:
int *(*(*(*a[N])( ))( ))[M];