pedir memoria en tiempo de ejecución y devolverla correctamente.
Incluye 4 ejercicios con pistas y solución y un quiz de 6 preguntas. Tiempo estimado: 90 minutos.
1. Pila vs. montón
| Pila (stack) | Montón (heap) | |
|---|---|---|
| Qué guarda | variables locales, parámetros | lo que pides con malloc |
| Gestión | automática | manual (malloc/free) |
| Tamaño | pequeño (≈ 1-8 MB) | grande (limitado por la RAM) |
| Dura | hasta salir de la función | hasta que hagas free |
Un int v[1000000]; local puede reventar la pila. Y los arrays de tamaño fijo no sirven si no sabes cuántos datos habrá: ahí entra el heap.
2. Las cuatro funciones (<stdlib.h>)
int *v = malloc(n * sizeof *v); // n enteros SIN inicializar
int *w = calloc(n, sizeof *w); // n enteros a CERO
v = realloc(v, 2 * n * sizeof *v); // cambia el tamaño (ver abajo: forma segura)
free(v); // devuelve la memoria
mallocrecibe bytes.sizeof *vse adapta si cambias el tipo (mejor quesizeof(int)).- Devuelve
NULLsi no hay memoria → compruébalo. - Tras
malloc,vse usa como un array normal:v[i].
Patrón de uso completo
int *v = malloc(n * sizeof *v);
if (v == NULL) { fprintf(stderr, "sin memoria\n"); return 1; }
... usar v ...
free(v);
v = NULL; // evita usarlo "después de liberar" por accidente
realloc seguro
int *tmp = realloc(v, nuevo * sizeof *v);
if (tmp == NULL) { free(v); return 1; } // v seguía siendo válido
v = tmp;
Si hicieras v = realloc(v, ...) y fallara, perderías el único puntero al bloque original (fuga).
Estrategia de crecimiento: duplica la capacidad cuando se llene (cap *= 2): el coste amortizado por inserción es constante.
3. Regla de oro de la propiedad
Cada
mallocdebe tener exactamente unfree.
Decide siempre quién es dueño de la memoria. Convención clara: quien recibe el puntero de una función crear_... es quien libera.
4. Los errores clásicos
| Error | Consecuencia |
|---|---|
Olvidar free |
fuga de memoria (el programa engorda hasta morir) |
free dos veces (double free) |
crash/corrupción |
Usar tras free (use-after-free) |
comportamiento indefinido |
| Escribir fuera del bloque (overflow) | corrupción del heap |
free de algo que no vino de malloc |
crash |
Olvidar el +1 del '\0' en cadenas |
desbordamiento de 1 byte |
5. Cadenas dinámicas
char *copia = malloc(strlen(s) + 1); // +1 para '\0'
strcpy(copia, s);
...
free(copia);
6. Detectar errores de memoria
- AddressSanitizer: compila con
-fsanitize=address -g(gcc/clang). Detecta overflows, use-after-free y fugas. - Valgrind (Linux/WSL):
valgrind --leak-check=full ./prog. - Truco casero: cuenta mallocs y frees con contadores en una función envoltorio.
Prueba esto
- Reserva 1000 enteros con
calloc, comprueba que están a 0, y libéralos. - Provoca una fuga a propósito (malloc sin free en un bucle de 1e6 iteraciones) y mira cómo sube el uso de memoria en el Administrador de tareas.
- Escribe
char *leer_linea(void)que usereallocpara crecer hasta el'\n'.
Ejercicios prácticos del módulo 8
Programar se aprende programando: intenta cada ejercicio antes de mirar las pistas, y usa la solución solo para comparar. Marca los que resuelvas y tu progreso se guarda en este navegador.
8.1 Array del tamano justo
Entrada de ejemplo
4 1 2 3 4
Salida esperada
4 3 2 1
Tu punto de partida
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n;
scanf("%d", &n);
// TODO: int *v = malloc(...)
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
malloc recibe BYTES: n * sizeof(int) (o mejor n * sizeof *v).
Pista 2
Despues de reservar, v se usa exactamente igual que un array: v[i].
Ver solución (inténtalo antes)
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n;
scanf("%d", &n);
int *v = malloc(n * sizeof *v);
if (v == NULL) return 1;
for (int i = 0; i < n; i++) scanf("%d", &v[i]);
for (int i = n - 1; i >= 0; i--) printf("%d ", v[i]);
printf("\n");
free(v);
return 0;
}8.2 Una funcion que devuelve memoria
Entrada de ejemplo
3 7
Salida esperada
3 4 5 6 7
Tu punto de partida
#include <stdio.h>
#include <stdlib.h>
int *crear_rango(int a, int b) {
// TODO
return NULL;
}
int main(void) {
int a, b;
scanf("%d %d", &a, &b);
int *r = crear_rango(a, b);
for (int i = 0; i <= b - a; i++) printf("%d ", r[i]);
printf("\n");
free(r);
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
Numero de elementos: b - a + 1.
Pista 2
Aqui SI puedes devolver el puntero: la memoria del heap sobrevive a la funcion (a diferencia de una local).
Ver solución (inténtalo antes)
#include <stdio.h>
#include <stdlib.h>
int *crear_rango(int a, int b) {
int n = b - a + 1;
int *r = malloc(n * sizeof *r);
if (r == NULL) return NULL;
for (int i = 0; i < n; i++) r[i] = a + i;
return r;
}
int main(void) {
int a, b;
scanf("%d %d", &a, &b);
int *r = crear_rango(a, b);
for (int i = 0; i <= b - a; i++) printf("%d ", r[i]);
printf("\n");
free(r);
return 0;
}8.3 Cantidad desconocida: realloc
Entrada de ejemplo
3 8 1 4 5 0
Salida esperada
n=5 suma=21 mayor=8
Tu punto de partida
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int cap = 2, n = 0;
int *v = malloc(cap * sizeof *v);
int x;
while (scanf("%d", &x) == 1 && x != 0) {
// TODO: si n == cap, duplica cap y haz realloc. Despues guarda x.
}
// TODO: calcula suma y mayor (si n > 0) e imprime
free(v);
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
realloc(ptr, nuevos_bytes) devuelve el nuevo puntero: asignalo a un puntero TEMPORAL primero.
Pista 2
Si realloc falla devuelve NULL y el bloque original sigue vivo: si lo asignas directo a v pierdes la memoria.
Ver solución (inténtalo antes)
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int cap = 2, n = 0;
int *v = malloc(cap * sizeof *v);
int x;
while (scanf("%d", &x) == 1 && x != 0) {
if (n == cap) {
cap *= 2;
int *tmp = realloc(v, cap * sizeof *v);
if (tmp == NULL) { free(v); return 1; }
v = tmp;
}
v[n++] = x;
}
long long suma = 0;
int mayor = n > 0 ? v[0] : 0;
for (int i = 0; i < n; i++) {
suma += v[i];
if (v[i] > mayor) mayor = v[i];
}
printf("n=%d suma=%lld mayor=%d\n", n, suma, mayor);
free(v);
return 0;
}8.4 Cadenas dinamicas
Entrada de ejemplo
hola mundo
Salida esperada
hola|holamundo
Tu punto de partida
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *duplicar(const char *s) {
// TODO
return NULL;
}
char *unir(const char *a, const char *b) {
// TODO
return NULL;
}
int main(void) {
char a[100], b[100];
scanf("%99s %99s", a, b);
char *d = duplicar(a);
char *u = unir(a, b);
printf("%s|%s\n", d, u);
free(d);
free(u);
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
El tamano necesario es strlen + 1 (por el terminador).
Pista 2
unir: strlen(a) + strlen(b) + 1, luego strcpy(r, a) y strcat(r, b).
Ver solución (inténtalo antes)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *duplicar(const char *s) {
char *r = malloc(strlen(s) + 1);
if (r) strcpy(r, s);
return r;
}
char *unir(const char *a, const char *b) {
char *r = malloc(strlen(a) + strlen(b) + 1);
if (r) {
strcpy(r, a);
strcat(r, b);
}
return r;
}
int main(void) {
char a[100], b[100];
scanf("%99s %99s", a, b);
char *d = duplicar(a);
char *u = unir(a, b);
printf("%s|%s\n", d, u);
free(d);
free(u);
return 0;
}Quiz del módulo 8
6 preguntas para comprobar que has fijado los conceptos. Las opciones se barajan cada vez.