Perché le definizioni dei puntatori a funzione funzionano con un numero qualsiasi di e commerciali '&' o asterischi '*'?


216

Perché il seguente lavoro?

void foo() {
    cout << "Foo to you too!\n";
};

int main() {
    void (*p1_foo)() = foo;
    void (*p2_foo)() = *foo;
    void (*p3_foo)() = &foo;
    void (*p4_foo)() = *&foo;
    void (*p5_foo)() = &*foo;
    void (*p6_foo)() = **foo;
    void (*p7_foo)() = **********************foo;

    (*p1_foo)();
    (*p2_foo)();
    (*p3_foo)();
    (*p4_foo)();
    (*p5_foo)();
    (*p6_foo)();
    (*p7_foo)();
}

Risposte:


224

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.


2
@Jimmy: quelli non sono riferimenti a puntatori a funzione, sono solo puntatori a funzione. &fooprende l'indirizzo di foo, il che si traduce in un puntatore a funzione che punta a foo, come ci si aspetterebbe.
Dennis Zickefoose,

2
Non puoi neppure concatenare &operatori per oggetti: dato int p;, &prestituisce un puntatore a ped è un'espressione di valore; l' &operatore richiede un'espressione lvalue.
James McNellis,

12
Non sono d'accordo. Più *è, meno allegro .
Seth Carnegie,

28
Per favore non modificare la sintassi dei miei esempi. Ho scelto gli esempi in modo molto specifico per dimostrare le caratteristiche della lingua.
James McNellis il

7
Come nota a margine, lo standard C afferma esplicitamente che una combinazione di &*annullamento reciproco (6.5.3.2): "The unary & operator yields the address of its operand."/ - / "If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.".
Lundin,

9

Penso che sia anche utile ricordare che C è solo un'astrazione per la macchina sottostante e questo è uno dei luoghi in cui quell'astrazione perde.

Dal punto di vista del computer, una funzione è solo un indirizzo di memoria che, se eseguito, esegue altre istruzioni. Quindi una funzione in C è modellata come un indirizzo, il che probabilmente porta alla progettazione che una funzione è "la stessa" dell'indirizzo a cui punta.


0

&e *sono operazioni idempotenti su un simbolo dichiarato come una funzione in C che significa func == *func == &func == *&funce quindi*func == **func

Significa che il tipo int ()è lo stesso di int (*)()un parametro di funzione e una funzione definita può essere passata come *func, funco &func. (&func)()è lo stesso di func(). Link Godbolt.

Una funzione è davvero un indirizzo, quindi *e &non ha alcun significato, e invece di produrre un errore, il compilatore sceglie di interpretarlo come l'indirizzo di func.

&su un simbolo dichiarato come puntatore a funzione otterrà comunque l'indirizzo del puntatore (perché ora ha uno scopo separato), mentre funcpe *funcpsarà identico

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.