La regla de "espiral" se cae de las siguientes reglas de precedencia:
T *a[] -- a is an array of pointer to T
T (*a)[] -- a is a pointer to an 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
Los operadores de []llamadas de subíndice y función ()tienen mayor prioridad que los unarios *, por lo que *f()se analiza como *(f())y *a[]se analiza como *(a[]).
Entonces, si desea un puntero a una matriz o un puntero a una función, entonces necesita agrupar explícitamente el *con el identificador, como en (*a)[]o (*f)().
Entonces te das cuenta de eso ay fpuedes ser expresiones más complicadas que solo identificadores; en T (*a)[N], apodría ser un identificador simple, o podría ser una función llamada como (*f())[N]( a-> f()), o podría ser una matriz como (*p[M])[N], ( a-> p[M]), o podría ser una matriz de punteros a funciones como (*(*p[M])())[N]( a-> (*p[M])()), etc.
Sería bueno si el operador de indirección *fuera postfix en lugar de unario, lo que facilitaría la lectura de las declaraciones de izquierda a derecha ( void f[]*()*();definitivamente fluye mejor que void (*(*f[])())()), pero no lo es.
Cuando encuentre una declaración peluda como esa, comience por encontrar el identificador más a la izquierda y aplique las reglas de precedencia anteriores, aplicándolas recursivamente a cualquier parámetro de función:
f -- f
f[] -- is an array
*f[] -- of pointers ([] has higher precedence than *)
(*f[])() -- to functions
*(*f[])() -- returning pointers
(*(*f[])())() -- to functions
void (*(*f[])())(); -- returning void
La signalfunción en la biblioteca estándar es probablemente el espécimen tipo para este tipo de locura:
signal -- signal
signal( ) -- is a function with parameters
signal( sig, ) -- sig
signal(int sig, ) -- which is an int and
signal(int sig, func ) -- func
signal(int sig, *func ) -- which is a pointer
signal(int sig, (*func)(int)) -- to a function taking 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)))(int) -- to a function taking an int
void (*signal(int sig, void (*func)(int)))(int); -- and returning void
En este punto, la mayoría de la gente dice "use typedefs", que sin duda es una opción:
typedef void outerfunc(void);
typedef outerfunc *innerfunc(void);
innerfunc *f[N];
Pero...
¿Cómo usarías f en una expresión? Sabes que es un conjunto de punteros, pero ¿cómo lo usas para ejecutar la función correcta? Tienes que revisar los typedefs y descifrar la sintaxis correcta. Por el contrario, la versión "desnuda" es bastante curiosa, pero le dice exactamente cómo usarla f en una expresión (es decir (*(*f[i])())();, suponiendo que ninguna función tome argumentos).