La regola "spirale" non rientra nelle seguenti regole di precedenza:
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
Gli operatori di sottoscrizione []
e chiamata di funzione ()
hanno una precedenza maggiore rispetto all'unario *
, quindi *f()
viene analizzato come *(f())
e *a[]
viene analizzato come *(a[])
.
Quindi, se si desidera un puntatore a un array o un puntatore a una funzione, è necessario raggruppare esplicitamente il carattere *
con l'identificatore, come in (*a)[]
o (*f)()
.
Quindi te ne rendi conto a
e f
possono essere espressioni più complicate di semplici identificatori; in T (*a)[N]
, a
potrebbe essere un semplice identificatore, oppure potrebbe essere una chiamata di funzione come (*f())[N]
( a
-> f()
), oppure potrebbe essere una matrice come (*p[M])[N]
, ( a
-> p[M]
), oppure potrebbe essere una matrice di puntatori a funzioni come (*(*p[M])())[N]
( a
-> (*p[M])()
), eccetera.
Sarebbe bello se l'operatore di riferimento indiretto *
fosse postfisso anziché unario, il che renderebbe le dichiarazioni un po 'più facili da leggere da sinistra a destra ( void f[]*()*();
scorre sicuramente meglio di void (*(*f[])())()
), ma non lo è.
Quando ti imbatti in una dichiarazione pelosa come quella, inizia trovando l' identificatore più a sinistra e applica le regole di precedenza sopra, applicandole in modo ricorsivo a qualsiasi parametro di funzione:
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
funzione nella libreria standard è probabilmente il tipo di campione per questo tipo di follia:
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
A questo punto la maggior parte delle persone dice "usa typedefs", che è certamente un'opzione:
typedef void outerfunc(void);
typedef outerfunc *innerfunc(void);
innerfunc *f[N];
Ma...
Come si usa f
in un'espressione? Sai che è un array di puntatori, ma come lo usi per eseguire la funzione corretta? Devi andare oltre i typedef e decifrare la sintassi corretta. Al contrario, la versione "nuda" è piuttosto accattivante, ma ti dice esattamente come usare f
in un'espressione (vale a dire (*(*f[i])())();
, supponendo che nessuna delle funzioni accetti argomenti).