A regra "espiral" cai das seguintes regras de precedência:
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
O subscrito []
()
operadores de e chamada de função têm maior precedência que unário *
, portanto, *f()
são analisados como *(f())
e *a[]
são analisados como *(a[])
.
Portanto, se você deseja um ponteiro para uma matriz ou um ponteiro para uma função, precisa agrupar explicitamente o *
com o identificador, como em (*a)[]
ou (*f)()
.
Então você percebe isso a
e f
pode ser expressões mais complicadas do que apenas identificadores; in T (*a)[N]
, a
poderia ser um identificador simples ou uma chamada de função como (*f())[N]
( a
->f()
), ou poderia ser uma matriz como (*p[M])[N]
, ( a
-> p[M]
), ou poderia ser uma matriz de ponteiros para funções como (*(*p[M])())[N]
( a
-> (*p[M])()
), etc.
Seria bom se o operador de indireção *
fosse postfix em vez de unário, o que tornaria as declarações um pouco mais fáceis de ler da esquerda para a direita ( void f[]*()*();
definitivamente flui melhor que void (*(*f[])())()
), mas não é.
Quando você se deparar com uma declaração cabeluda como essa, comece encontrando o identificador mais esquerda e aplique as regras de precedência acima, aplicando-as recursivamente a qualquer parâmetro de função:
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
o signal
função na biblioteca padrão é provavelmente o tipo de amostra para esse tipo de insanidade:
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
Neste ponto, a maioria das pessoas diz "use typedefs", o que certamente é uma opção:
typedef void outerfunc(void);
typedef outerfunc *innerfunc(void);
innerfunc *f[N];
Mas...
Como você usaria f
em uma expressão? Você sabe que é uma matriz de ponteiros, mas como você o utiliza para executar a função correta? Você precisa revisar os typedefs e descobrir a sintaxe correta. Por outro lado, a versão "nua" é bastante óbvia, mas mostra exatamente como usar f
uma expressão (ou seja (*(*f[i])())();
, supondo que nenhuma função use argumentos).