Punteros y direcciones de memoria
Al finalizar este tema
Comprenderá que un puntero es una "variable que almacena una dirección de memoria", y por qué es importante en la informática, así como cómo se abstrae en los lenguajes de alto nivel.
¿Dónde se almacenan las variables?
Cuando se ejecuta un programa, las variables se almacenan en la memoria. La memoria se organiza en unidades de bytes, y cada unidad tiene un número asignado. Este número es la dirección de memoria.
Dirección de memoria: 0x1000 0x1004 0x1008 0x100C
┌──────┐┌──────┐┌──────┐┌──────┐
│ 42 ││ 7 ││ 'A' ││ 0 │
└──────┘└──────┘└──────┘└──────┘
variable a variable b variable c variable dAl escribir a = 42, la computadora guarda el valor 42 en alguna ubicación de la memoria y asocia el nombre a con esa dirección. Nosotros solo necesitamos usar el nombre a, sin preocuparnos por la dirección.
Punteros: variables que almacenan direcciones
Las variables normales almacenan valores. Los punteros almacenan la dirección de otra variable.
int *p = &a; // Guarda la dirección de a en p
printf("%p\n", p); // 0x1000 (dirección de a)
printf("%d\n", *p); // 42 (valor al que apunta p)variable a puntero p
┌──────────┐ ┌──────────┐
│ 42 │ │ 0x1000 │──→ Apunta a a
└──────────┘ └──────────┘&a— obtener la dirección dea*p— obtener el valor al que apuntap
¿Por qué se necesitan los punteros?
1. Transmisión eficiente de grandes cantidades de datos
Supongamos que se pasa un arreglo con 10 millones de números a una función. Copiar todo el arreglo sería un desperdicio de memoria y tiempo. Si solo se pasa la dirección, se puede acceder directamente al original.
2. Estructuras de datos dinámicas
Listas enlazadas, árboles, grafos: estas estructuras de datos deben almacenar la "ubicación del siguiente nodo". Esa "ubicación" es un puntero.
nodo A nodo B nodo C
┌────┬────────┐ ┌────┬────────┐ ┌────┬──────┐
│ 10 │ 0x2000 │──→│ 20 │ 0x3000 │──→│ 30 │ NULL │
└────┴────────┘ └────┴────────┘ └────┴──────┘3. Modificación del objeto original dentro de una función.
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int x = 10, y = 20;
swap(&x, &y);
// x = 20, y = 10Si se pasa un valor por copia en lugar de usar un puntero, el valor original no se modificará, independientemente de los cambios que se realicen dentro de la función. Para modificar el valor original, es necesario pasar la dirección de memoria mediante un puntero.
Punteros peligrosos
Puntero nulo
int *p = NULL;
printf("%d", *p); // ¡Crash! (Segmentation Fault)Desreferenciar un puntero que no apunta a nada provoca que el programa termine de forma inesperada. Esto se conoce como desreferenciación de puntero nulo, y es uno de los tipos de errores más comunes y costosos.
Puntero colgante
int *p = malloc(sizeof(int));
*p = 42;
free(p); // Devolución de memoria
printf("%d", *p); // Memoria ya devuelta — comportamiento impredeciblefree al liberar la memoria y luego escribir en esa dirección, a veces se obtiene un valor incorrecto y, en el peor de los casos, se produce un error.
Punteros en lenguajes de alto nivel
En lenguajes de alto nivel como Python, JavaScript y Java, los punteros no se manipulan directamente. Sin embargo, se utiliza un concepto similar denominado referencia.
# Referencias en Pythona = [1, 2, 3]b = a # b "referencia" la misma listab.append(4)print(a) # [1, 2, 3, 4] — El original también cambiaLas variables de Python son, en realidad, punteros. Simplemente carecen de sintaxis como & o *, y no es posible manipular las direcciones de memoria directamente. El problema que se trató anteriormente en el tema de Python (propagación de referencias de objetos mutables) se refiere precisamente a esto.
| Puntero C | Referencia de Python | |
|---|---|---|
| Acceso directo a la dirección | Posible | Imposible |
| Operaciones con punteros | Posible (p + 1) | Imposible |
| Error por valor NULL | Posible | None → AttributeError |
| Liberación manual de memoria | Obligatorio (free) | Automática (recolección de basura) |
Los lenguajes de alto nivel ocultan y hacen que los punteros sean más seguros. A cambio, se sacrifica la capacidad de controlar directamente la memoria.
Puntos clave
Un puntero es una variable que almacena una dirección de memoria. Los punteros son necesarios para tres propósitos: pasar datos grandes, crear estructuras de datos dinámicas y modificar el original. La "referencia" en Python/JavaScript es un puntero envuelto de forma segura: no permite manipular la dirección, pero funciona según el mismo principio.