Ci sono alcuni pezzi che consentono a tutte queste combinazioni di operatori di lavorare allo stesso modo.
Il motivo fondamentale per cui tutti questi lavori è che una funzione (come foo) è implicitamente convertibile in un puntatore alla funzione. Ecco perché void (*p1_foo)() = foo;funziona: fooviene implicitamente convertito in un puntatore a se stesso e quel puntatore è assegnato a p1_foo.
L'unario &, quando applicato a una funzione, fornisce un puntatore alla funzione, proprio come produce l'indirizzo di un oggetto quando viene applicato a un oggetto. Per i puntatori a funzioni ordinarie, è sempre ridondante a causa della conversione implicita da funzione a funzione. In ogni caso, questo è il motivo per cui void (*p3_foo)() = &foo;funziona.
L'unario *, quando applicato a un puntatore a funzione, produce la funzione a punta, proprio come produce l'oggetto a punta quando viene applicato a un normale puntatore a un oggetto.
Queste regole possono essere combinate. Considera il tuo penultimo esempio **foo:
- Innanzitutto,
fooviene implicitamente convertito in un puntatore a se stesso e il primo *viene applicato a quel puntatore di funzione, restituendo foonuovamente la funzione .
- Quindi, il risultato viene nuovamente convertito implicitamente in un puntatore a se stesso e
*viene applicato il secondo , restituendo nuovamente la funzione foo.
- Viene quindi nuovamente implicitamente convertito in un puntatore a funzione e assegnato alla variabile.
Puoi aggiungere quanti *s vuoi, il risultato è sempre lo stesso. Più *s, più è bello.
Possiamo anche considerare il tuo quinto esempio &*foo:
- Innanzitutto,
fooviene implicitamente convertito in un puntatore a se stesso; *viene applicato l'unario , cedendo di foonuovo.
- Quindi,
&viene applicato a foo, restituendo un puntatore a foo, che viene assegnato alla variabile.
Tuttavia, &può essere applicato solo a una funzione, non a una funzione che è stata convertita in un puntatore a funzione (a meno che, ovviamente, il puntatore a funzione sia una variabile, nel qual caso il risultato è un puntatore a un puntatore- to-a-function; ad esempio, è possibile aggiungere all'elenco void (**pp_foo)() = &p7_foo;).
Ecco perché &&foonon funziona: &foonon è una funzione; è un puntatore a funzione che è un valore. Tuttavia, &*&*&*&*&*&*foofunzionerebbe, come farebbe &******&foo, perché in entrambe queste espressioni &viene sempre applicato a una funzione e non a un puntatore a funzione rvalore.
Si noti inoltre che non è necessario utilizzare unario *per effettuare la chiamata tramite il puntatore a funzione; entrambi (*p1_foo)();e (p1_foo)();hanno lo stesso risultato, sempre a causa della conversione da funzione a funzione-puntatore.
&fooprende l'indirizzo difoo, il che si traduce in un puntatore a funzione che punta afoo, come ci si aspetterebbe.