Normalmente, cuando se pasa un argumento a una función, se hace una copia de dicho argumento dentro de la función. Esto se llama paso-por-valor. Se puede ver el efecto de un paso-por-valor en el siguiente programa:
//: C03:PassByValue.cpp #include <iostream> using namespace std; void f(int a) { cout << "a = " << a << endl; a = 5; cout << "a = " << a << endl; } int main() { int x = 47; cout << "x = " << x << endl; f(x); cout << "x = " << x << endl; } ///:~
Listado 3.15. C03/PassByValue.cpp
En f()
, a
es una
variable local, de modo que existe únicamente mientras dura la
llamada a la función f()
. Como es un
argumento de una función, el valor de a
se
inicializa mediante los argumentos que se pasan en la invocación
de la función; en main()
el argumento es
x
, que tiene un valor 47, de modo que el
valor es copiado en a
cuando se llama a
f()
.
Cuando ejecute el programa verá:
x = 47 a = 47 a = 5 x = 47
Por supuesto, inicialmente x
es 47. Cuando se
llama f()
, se crea un espacio temporal para
alojar la variable a
durante la ejecución de
la función, y el valor de x
se copia a
a
, el cual es verificado mostrándolo por
pantalla. Se puede cambiar el valor de a
y
demostrar que ha cambiado. Pero cuando f()
termina, el espacio temporal que se había creado para
a
desaparece, y se puede observar que la
única conexión que existía entre a
y
x
ocurrió cuando el valor de
x
se copió en a
.
Cuando está dentro de f()
,
x
es el objeto externo
(mi terminología), y cambiar el valor de la variable local no
afecta al objeto externo, lo cual es bastante lógico, puesto que
son dos ubicaciones separadas en la memoria. Pero ¿y si quiere
modificar el objeto externo? Aquí es donde los punteros entran
en acción. En cierto sentido, un puntero es un alias de otra
variable. De modo que si a una función se le pasa un puntero en
lugar de un valor ordinario, se está pasando de hecho un alias
del objeto externo, dando la posibilidad a la función de que
pueda modificar el objeto externo, tal como sigue:
//: C03:PassAddress.cpp #include <iostream> using namespace std; void f(int* p) { cout << "p = " << p << endl; cout << "*p = " << *p << endl; *p = 5; cout << "p = " << p << endl; } int main() { int x = 47; cout << "x = " << x << endl; cout << "&x = " << &x << endl; f(&x); cout << "x = " << x << endl; } ///:~
Listado 3.16. C03/PassAddress.cpp
Ahora f()
toma el puntero como un argumento
y dereferencia el puntero durante la asignación, lo que modifica
el objeto externo x
. La salida es:
x = 47 &x = 0065FE00 p = 0065FE00 *p = 47 p = 0065FE00 x = 5
Tenga en cuenta que el valor contenido en p
es el mismo que la dirección de x
- el
puntero p
de hecho apunta a
x
. Si esto no es suficientemente convincente,
cuando p
es dereferenciado para asignarle el
valor 5, se ve que el valor de x
cambia a 5
también.
De ese modo, pasando un puntero a una función le permitirá a esa función modificar el objeto externo. Se verán muchos otros usos de los punteros más adelante, pero podría decirse que éste es el más básico y posiblemente el más común.