Il modo migliore per rispondere a questa è quello di guardare lo smontaggio (campione leggermente modificata):
fptr1 = □
int result1 = fptr1(5);
int result2 = square(5);
I risultati in questo x64 ASM:
fptr1 = □
000000013FA31A61 lea rax,[square (013FA31037h)]
000000013FA31A68 mov qword ptr [fptr1 (013FA40290h)],rax
int result1 = fptr1(5);
000000013FA31A6F mov ecx,5
000000013FA31A74 call qword ptr [fptr1 (013FA40290h)]
000000013FA31A7A mov dword ptr [result1],eax
int result2 = square(5);
000000013FA31A7E mov ecx,5
000000013FA31A83 call square (013FA31037h)
000000013FA31A88 mov dword ptr [result2],eax
Come si può vedere l'assemblaggio è praticamente identico tra chiamare la funzione direttamente e tramite un puntatore.In entrambi i casi, la CPU deve avere accesso alla posizione in cui si trova il codice e chiamarla. La chiamata diretta ha il vantaggio di non dover dereferenziare il puntatore (poiché l'offset verrà inserito nell'assieme).
- Sì, si può vedere nella assegnazione del puntatore a funzione, che memorizza l'indirizzo codice della funzione 'piazza'.
- Da una pila installazione/demolizione: sì. Dal punto di vista delle prestazioni, vi è una leggera differenza come indicato sopra.
- Non ci sono rami, quindi non c'è differenza qui.
Edit: Se dovessimo interporre rami nel campione di cui sopra, non ci sarebbe voluto molto tempo per esaurire gli scenari interessanti, così io li affrontare qui:
Nel caso in cui ci avere un ramo prima di caricare (o assegnazione) del puntatore funzione, per esempio (a montaggio pseudo):
branch zero foobar
lea square
call ptr
Poi potrebbe avere una differenza. Supponiamo che la pipeline abbia scelto di caricare e avviare l'elaborazione delle istruzioni su foobar
, quindi quando ha capito che non avremmo effettivamente preso quel ramo, dovrebbe essere bloccato per caricare il puntatore di funzione e dereferenziarlo. Se stessimo solo chiamando un indirizzo di conoscenza, non ci sarebbe stato uno stallo.
Caso due:
lea square
branch zero foobar
call ptr
In questo caso non ci sarebbe alcuna differenza tra le chiamate dirette vs attraverso un puntatore a funzione, come tutto ciò che abbiamo bisogno è già sapere se il processore inizia l'esecuzione sulla strada sbagliata e quindi si reimposta per avviare l'esecuzione della chiamata.
Il terzo scenario è quando il ramo segue la chiamata, e ovviamente non è molto interessante da una prospettiva della pipeline dato che abbiamo già eseguito la subroutine.
Quindi per rispondere completamente alla domanda , direi Sì, c'è una differenza. Ma la vera domanda è se il compilatore/ottimizzatore è abbastanza intelligente da spostare il ramo dopo il l'assegnazione del puntatore di funzione, così rientra nel caso 2 e non nel caso 1.
[Ecco un progetto per aiutare a spiegare l'utilità dei puntatori di funzione] (http://www.cs.rit.edu/~ats/books/ooc.pdf) –
Si noti che 'void main()' non è valido in C (o C++) - non in standard C, in C basata su Unix, né in Microsoft C. Dovrebbe essere 'int main()'. –
Grazie per l'avviso su main – cycrel