Disculpe el uso del ensamblado para explicar esto, pero creo que esta es la mejor manera de entender cómo los compiladores implementan las referencias.
#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;
}
salida de este código es como esto
i = 10
&i = 0xbf9e52f8
ptrToI = 0xbf9e52f8
*ptrToI = 10
&ptrToI = 0xbf9e52f4
refToNum = 10
&refToNum = 0xbf9e52f8
Veamos el desmontaje (he usado para este BGF. 8,9 y 10 aquí están los números de línea de código)
8 int i = 10;
0x08048698 <main()+18>: movl $0xa,-0x10(%ebp)
Aquí $0xa
es el 10 (decimal) que estamos asignando a i
. -0x10(%ebp)
aquí significa contenido de ebp register
-16 (decimal). -0x10(%ebp)
apunta a la dirección de i
en la pila.
9 int *ptrToI = &i;
0x0804869f <main()+25>: lea -0x10(%ebp),%eax
0x080486a2 <main()+28>: mov %eax,-0x14(%ebp)
Asignar dirección de i
a ptrToI
. ptrToI
está de nuevo en la pila ubicada en la dirección -0x14(%ebp)
, que es ebp
- 20 (decimal).
10 int &refToI = i;
0x080486a5 <main()+31>: lea -0x10(%ebp),%eax
0x080486a8 <main()+34>: mov %eax,-0xc(%ebp)
Ahora aquí está el truco! Compare el desmontaje de la línea 9 y 10 y observará que -0x14(%ebp)
se reemplazó por -0xc(%ebp)
en la línea número 10. -0xc(%ebp)
es la dirección de refToNum. Se asigna en la pila. Pero nunca podrá obtener esta dirección de su código porque no es necesario que conozca la dirección.
So; una referencia ocupa memoria. En este caso, es la memoria de la pila ya que la hemos asignado como una variable local. ¿Cuánta memoria ocupa? Tanto un puntero ocupa.
Ahora veamos cómo accedemos a la referencia y los punteros. Por simplicidad he mostrado sólo una parte del fragmento de ensamblaje
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
Ahora compare las dos líneas anteriores, verá sorprendente similitud. -0xc(%ebp)
es la dirección real de refToI
a la que nunca puede acceder. En términos simples, si considera que la referencia es un puntero normal, acceder a una referencia es como obtener el valor en la dirección a la que apunta la referencia. Lo que significa que los siguientes dos líneas de código le dará el mismo resultado
cout << "Value if i = " << *ptrToI << "\n";
cout << " Value if i = " << refToI << "\n";
Ahora comparar este
15 cout << "ptrToI = " << ptrToI << "\n";
0x08048713 <main()+141>: mov -0x14(%ebp),%ebx
21 cout << "&refToNum = " << &refToI << "\n";
0x080487fb <main()+373>: mov -0xc(%ebp),%eax
supongo que son capaces de detectar lo que está sucediendo aquí. Si solicita &refToI
, se devuelve el contenido de la dirección -0xc(%ebp)
y -0xc(%ebp)
donde reside refToi
y su contenido no es más que la dirección de i
.
Una última cosa, ¿Por qué se comenta esta línea?
//cout << "*refToNum = " << *refToI << "\n";
Debido *refToI
no está permitida y se le dará un error de tiempo de compilación.
"Sentí que un entendimiento en el fondo de ese nivel me haría entender mejor el concepto de puntero contra referencia" No creo que eso ayude. –
Puedes probar esto: http://eetimes.com/discussion/programming-pointers/4023307/References-vs-Pointers – Vlad
@ MR.Anubis ..... ¿qué quieres decir? – howtechstuffworks