3.4.5. Modificar objetos externos

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.