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 a
y f
puedes ser expresiones más complicadas que solo identificadores; en T (*a)[N]
, a
podrí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 signal
funció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).