Siamo spiacenti per l'utilizzo di assemblaggio di spiegare questo, ma penso che questo sia il modo migliore per capire come i riferimenti sono implementate dai compilatori.
#include <iostream>
using namespace std;
int main()
{
int i = 10;
int *ptrToI = &i;
int &refToI = i;
cout << "i = " << i << "\n";
cout << "&i = " << &i << "\n";
cout << "ptrToI = " << ptrToI << "\n";
cout << "*ptrToI = " << *ptrToI << "\n";
cout << "&ptrToI = " << &ptrToI << "\n";
cout << "refToNum = " << refToI << "\n";
//cout << "*refToNum = " << *refToI << "\n";
cout << "&refToNum = " << &refToI << "\n";
return 0;
}
uscita di questo codice è come questo
i = 10
&i = 0xbf9e52f8
ptrToI = 0xbf9e52f8
*ptrToI = 10
&ptrToI = 0xbf9e52f4
refToNum = 10
&refToNum = 0xbf9e52f8
Vediamo lo smontaggio (io ho usato GDB per questo. 8,9 e 10 qui sono i numeri di riga di codice)
8 int i = 10;
0x08048698 <main()+18>: movl $0xa,-0x10(%ebp)
Qui $0xa
è il 10 (decimale) che assegniamo a i
. -0x10(%ebp)
qui indica il contenuto di ebp register
-16 (decimale). -0x10(%ebp)
indica l'indirizzo di i
sullo stack.
9 int *ptrToI = &i;
0x0804869f <main()+25>: lea -0x10(%ebp),%eax
0x080486a2 <main()+28>: mov %eax,-0x14(%ebp)
Assegnare indirizzo di i
a ptrToI
. ptrToI
è di nuovo nello stack situato all'indirizzo -0x14(%ebp)
, ovvero ebp
-20 (decimale).
10 int &refToI = i;
0x080486a5 <main()+31>: lea -0x10(%ebp),%eax
0x080486a8 <main()+34>: mov %eax,-0xc(%ebp)
Ora ecco il problema! Confronta lo smontaggio delle linee 9 e 10 e osserverai che, -0x14(%ebp)
è stato sostituito da -0xc(%ebp)
nella riga numero -0xc(%ebp)
è l'indirizzo di refNoNum. È assegnato in pila. Ma non sarai mai in grado di ottenere questo indirizzo dal tuo codice perché non ti è richiesto di conoscere l'indirizzo.
Così; un riferimento occupa memoria. In questo caso è la memoria dello stack poiché l'abbiamo allocata come variabile locale. Quanta memoria occupa? Quanto un puntatore occupa.
Ora vediamo come accediamo al riferimento e ai puntatori. Per semplicità ho mostrato solo una parte del frammento di assemblaggio
16 cout << "*ptrToI = " << *ptrToI << "\n";
0x08048746 <main()+192>: mov -0x14(%ebp),%eax
0x08048749 <main()+195>: mov (%eax),%ebx
19 cout << "refToNum = " << refToI << "\n";
0x080487b0 <main()+298>: mov -0xc(%ebp),%eax
0x080487b3 <main()+301>: mov (%eax),%ebx
Ora confrontare queste due linee, si vedrà sorprendente somiglianza. -0xc(%ebp)
è l'indirizzo effettivo di refToI
che non è mai accessibile a te. In termini semplici, se si pensa al riferimento come puntatore normale, accedere a un riferimento è come recuperare il valore all'indirizzo indicato dal riferimento. Il che significa che le sotto due righe di codice vi darà lo stesso risultato
cout << "Value if i = " << *ptrToI << "\n";
cout << " Value if i = " << refToI << "\n";
Ora confrontare questo
15 cout << "ptrToI = " << ptrToI << "\n";
0x08048713 <main()+141>: mov -0x14(%ebp),%ebx
21 cout << "&refToNum = " << &refToI << "\n";
0x080487fb <main()+373>: mov -0xc(%ebp),%eax
Credo che si è in grado di individuare ciò che sta accadendo qui. Se si richiede &refToI
, il contenuto della posizione indirizzo -0xc(%ebp)
viene restituito e -0xc(%ebp)
è dove risiede refToi
e il suo contenuto non è altro che l'indirizzo di i
.
Un'ultima cosa, perché questa linea è stata commentata?
//cout << "*refToNum = " << *refToI << "\n";
Perché *refToI
non è permesso e vi darà un errore di tempo di compilazione.
"Sentivo che una comprensione in profondità in quel livello mi avrebbe fatto capire meglio il concetto di puntatore vs riferimento" Non credo che sarebbe di alcun aiuto. –
Puoi provare questo: http://eetimes.com/discussion/programming-pointers/4023307/References-vs-Pointers – Vlad
@ MR.Anubis ..... cosa intendi? – howtechstuffworks