Take the following snippet as an example.
char* const (*(* const bar)[5])(int)
Cannot seem to make sense of it or more so, cannot identify the initial point from where to begin making sense of it.
Take the following snippet as an example.
char* const (*(* const bar)[5])(int)
Cannot seem to make sense of it or more so, cannot identify the initial point from where to begin making sense of it.
Let's consider the declaration step by steap
char* const (*(* const bar)[5])(int);
This part
* const bar
declares a constant pointer with the name bar
that points to to an array of 5 elements of a pointer type
*(* const bar)[5]
that is pointer to the function type
char* const (int)
Here is a demonstration program.
#include <stdio.h>
char * const f1( int n )
{
printf( "f1 called with n = %d\n", n );
return NULL;
}
char *const f2( int n )
{
printf( "f2 called with n = %d\n", n );
return NULL;
}
char *const f3( int n )
{
printf( "f3 called with n = %d\n", n );
return NULL;
}
char *const f4( int n )
{
printf( "f4 called with n = %d\n", n );
return NULL;
}
char *const f5( int n )
{
printf( "f5 called with n = %d\n", n );
return NULL;
}
int main( void )
{
char *const ( *a[5] )( int ) = { f1, f2, f3, f4, f5 };
for (int i = 0; i < 5; i++)
{
a[i]( i );
}
putchar( '\n' );
char *const ( *( *const bar )[5] )( int ) = &a;
for (int i = 0; i < 5; i++)
{
( *bar )[i]( i );
}
}
The program output is
f1 called with n = 0
f2 called with n = 1
f3 called with n = 2
f4 called with n = 3
f5 called with n = 4
f1 called with n = 0
f2 called with n = 1
f3 called with n = 2
f4 called with n = 3
f5 called with n = 4
Using typedef declarations could make the declaration of the pointer much more readable.
The "declaration reflect use" says that:
The expression *(*(*bar)[0])(0)
gives a char
.
We can continue saying:
The expression (*(*bar)[0])(0)
gives a pointer to a char
.
So:
Calling function (*(*bar)[0])
with (int) parameter returns a pointer to a char
.
...
dereference bar
then indexes in array (sized 5) to have a pointer on a function with (int) parameter will returns a pointer to a char
.
The expression can also be decoded starting with the object. Here we have the name bar
as a start. But in some expressions we do not have any name, the start point is the first expression (in parentheses) that has at its right an array[...] or a function call(...) or nothing.
Note that to read an expression we 1st read part at right (arrays and functions), and finish reading 1st at left then other at left (pointers).
(* const bar)
, bar
is constant pointer to...bbar
, we have char* const (* bbar[5])(int);
(* bbar[5])
, bbar
is an array of 5 pointers to...bbbar
, we have char* const bbbar(int);
char* const bbbar(int)
, bbbar
is a function receiving an int
and returning a constant pointer to a char
.bar
is a constant pointer to an array of 5 pointers to functions receiving (int
) returning constant pointer to char
.
In real we never have to do that, complex expressions are typedef
:
typedef char* const (* FunctionPtr)(int);
typedef FunctionPtr ArrayOfFctPtr[5];
ArrayOfFctPtr* const bar;