Asignación dinámica de memoria: malloc y free
Al finalizar este tema
Comprenderá las diferencias entre la memoria de pila (stack) y la de montón (heap), por qué es necesaria la asignación dinámica y por qué se producen las fugas de memoria.
Dos tipos de memoria
La memoria utilizada por un programa se divide principalmente en dos regiones: la pila (stack) y el montón (heap).
Dirección alta ┌──────────────┐
│ Stack │ ← Asignación/liberación automática cada vez que se llama a una función
│ ↓ hacia abajo │
│ ↑ hacia arriba │
│ │
│ │
│ Heap │ ← Asignación/liberación directa por el programador
│ Heap │ ← Asignación/liberación directa por el programador
Dirección baja └──────────────┘| Pila | Montón | |
|---|---|---|
| Asignación | Automática (al llamar a la función) | Manual (malloc) |
| Liberación | Automática (al finalizar la función) | Manual (free) |
| Tamaño | Pequeño y fijo (normalmente unos pocos MB) | Grande y dinámico (hasta el límite de la RAM) |
| Velocidad | Muy rápida | Ligeramente más lenta |
| Uso | Variables locales, argumentos de función | Datos cuyo tamaño se desconoce |
¿Por qué es necesaria la asignación dinámica?
Si creas una variable en la pila, debes conocer su tamaño en tiempo de compilación:
int arr[100]; // 100 elementos — determinado en tiempo de compilaciónSin embargo, si el usuario pregunta: «¿Cuántos datos va a ingresar?», no se puede saber la respuesta hasta que se ejecute el programa. En estos casos, se utiliza la asignación dinámica:
int n;
printf("¿Cuántos? ");
scanf("%d", &n);
int *arr = malloc(n * sizeof(int)); // Asignar n elementos durante la ejecuciónmalloc(asignación de memoria) asigna la cantidad de bytes solicitada desde el área de memoria dinámica (heap).
malloc y free
#include <stdlib.h>
// Asignación
int *data = malloc(5 * sizeof(int));
// uso
for (int i = 0; i < 5; i++) {
data[i] = i * 10;
}
// liberación
free(data);
data = NULL; // asignar a NULL para mayor seguridadSi se proporciona malloc, se puede usar la memoria y, una vez que se termine de usar, se debe devolver a free. Si no se realiza free, esa memoria no se liberará hasta que finalice el programa.
Fuga de memoria
void process() {
int *data = malloc(1000000 * sizeof(int));
// procesamiento de datos...
if (error) {
return; // salir sin liberar!
}
free(data);
}Si esta función termina con un error, free no se ejecuta. No se liberan 1 millón de enteros (aproximadamente 4 MB). Si esta función se llama 1000 veces, se pierden 4 GB de memoria. Esto es una fuga de memoria (memory leak).
Las fugas de memoria son peligrosas porque el programa no se cierra inmediatamente. Consume memoria gradualmente hasta que todo el sistema se vuelve lento y, finalmente, falla por falta de memoria (OOM). En los programas de servidor, esto puede provocar fallos repentinos después de unos días.
Solución en lenguajes de alto nivel: recolección de basura
Python, JavaScript, Java y Go tienen un recolector de basura (GC) que se encarga de liberar la memoria automáticamente:
def process(): data = [0] * 1000000 # Al finalizar la función, no queda ninguna variable que referencie a data # → GC recupera automáticamente la memoriaEl programador no necesita llamar a free. El recolector de basura (GC) verifica periódicamente si “ninguna variable apunta a esta memoria” y la recupera.
El costo es:
| Manual (C) | Automático (Python/Java) | |
|---|---|---|
| Posibilidad de fugas de memoria | Alta (error del programador) | Baja (el GC se encarga) |
| Rendimiento | Predecible | Puede haber una breve pausa durante la ejecución del GC |
| Complejidad del código | Alta (gestión de free) | Baja (no requiere atención) |
Rust ha elegido un tercer camino: ni GC ni free manual; el compilador rastrea el ciclo de vida de las variables e inserta automáticamente el código de liberación.
Esencial
La pila se asigna y libera automáticamente, mientras que el montón es gestionado directamente por el programador. Se asigna en el montón con
mallocy se libera confree; omitirlo provoca una fuga de memoria. Los lenguajes de alto nivel resuelven este problema automáticamente mediante GC, pero comprender por qué es necesaria la gestión de memoria es fundamental en la informática.