¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Pila y montón: malloc y free en C

Después de esta lección: Puedo decir dónde vive una variable y qué hacen malloc y free.

Lección de 60 min12 comandosLaboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

Cuántos alumnos caben en una lista

En C, una variable vive en la pila o en el montón, y de dónde viva depende cuánto dura. La pila se limpia sola cuando acaba una función, y el montón lo gestionas tú con malloc y free.

Un programa de matrícula no sabe cuántos alumnos leerá: pueden ser 30 o 3.000. No puede reservar el sitio al compilar, así que lo pide al montón mientras corre.

Al final de esta lección

Nota

Puedo decir dónde vive una variable y qué hacen malloc y free.

Palabras clave

PalabraQué significaEjemplo
Pilala memoria de las variables locales de cada función
Marco de pilael bloque de la pila que usa una llamada
Montónla memoria que pides y devuelves tú, también llamada heap
mallocpide al montón un bloque de bytesmalloc(20)
freedevuelve un bloque al montónfree(p)
Fuga de memoriaun bloque pedido que nunca se devuelve

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Cada llamada recibe un marco de pila

Cuando llamas a una función, el programa reserva un marco de pila con sus variables locales. Al volver, el marco desaparece.

La pila funciona como una torre de platos: el último marco en llegar es el primero en irse. Es rápida y no hay que limpiarla. Pero una variable local no sobrevive a su función.

Ejemplo resuelto: dos llamadas a cuenta

MomentoMarcos en la pilaQué vale local
Empieza mainmainno existe
main llama a cuenta(3)main, cuenta6
cuenta devuelve 6mainel marco se ha ido
main llama a cuenta(10)main, cuenta20, en un marco nuevo
cuenta devuelve 20mainel marco se ha ido

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Entra en la carpeta de trabajo

Comando
mkdir -p ~/c && cd ~/c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe pila.c: dos llamadas a cuenta

Comando
printf '#include <stdio.h>\nint cuenta(int n) {\n  int local = n * 2;\n  return local;\n}\nint main(void) {\n  int a = cuenta(3);\n  int b = cuenta(10);\n  printf("%%d %%d\\n", a, b);\n  return 0;\n}\n' > pila.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Compila pila.c con avisos

Comando
gcc -Wall -o pila pila.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta pila: 6 y 20

Comando
./pila

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

malloc pide un bloque y free lo devuelve

El montón es memoria que pides tú. malloc(n) reserva n bytes y devuelve un puntero al primero, o NULL si no queda sitio.

El bloque dura hasta que llamas a free con ese puntero. No le importa qué función lo pidió. Por eso un programa puede pedir sitio para un número de alumnos que solo sabe al ejecutarse.

Escribe mon.c: un vector en el montón

Comando
printf '#include <stdio.h>\n#include <stdlib.h>\nint main(void) {\n  int n = 5;\n  int *v = malloc(n * sizeof(int));\n  if (v == NULL) return 1;\n  int suma = 0;\n  for (int i = 0; i < n; i++) { v[i] = i * 2; suma = suma + v[i]; }\n  printf("%%d\\n", suma);\n  free(v);\n  return 0;\n}\n' > mon.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Compila mon.c

Comando
gcc -Wall -o mon mon.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta mon: la suma es 20

Comando
./mon

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

El montón como una fila de casillas

Código: python
# El montón como una fila de casillas: . libre, # ocupada.
heap = [0] * 10
# Dos bloques pedidos: uno de 3 casillas y otro de 4.
for cell in range(0, 3):
    heap[cell] = 1
for cell in range(3, 7):
    heap[cell] = 1
picture = ""
for cell in heap:
    if cell == 1:
        picture = picture + "#"
    else:
        picture = picture + "."
print(picture)
# Pon a 0 las casillas 0, 1 y 2 antes de dibujar: es lo que hace free.

Tres errores clásicos con el montón

Una fuga de memoria es pedir un bloque y no devolverlo nunca: el programa gasta cada vez más. El uso tras free es leer o escribir un bloque ya devuelto. El doble free es devolver dos veces el mismo bloque.

Con la pila hay un error parecido: devolver la dirección de una variable local, que ya no existe.

Qué hacer con cada error

ErrorQué ocurreCómo se evita
Fuga de memoriaEl programa gasta memoria sin pararUn free por cada malloc
Uso tras freeLee o escribe memoria que ya no es suyaPoner p = NULL tras free(p)
Doble freeEl montón se corrompePoner p = NULL tras free(p)
Devolver &localApunta a un marco que ya no existeDevolver el valor, o usar malloc

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Escribe fuga.c, que cuenta pedidos y devoluciones

Comando
printf '#include <stdio.h>\n#include <stdlib.h>\nstatic int pedidos = 0, liberados = 0;\nint *pide(void) { pedidos++; return malloc(sizeof(int)); }\nvoid suelta(int *p) { liberados++; free(p); }\nint main(void) {\n  int *a = pide();\n  int *b = pide();\n  int *c = pide();\n  suelta(a);\n  suelta(c);\n  printf("pedidos %%d, liberados %%d, sin liberar %%d\\n", pedidos, liberados, pedidos - liberados);\n  (void)b;\n  return 0;\n}\n' > fuga.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Compila fuga.c

Comando
gcc -Wall -o fuga fuga.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta fuga: una sin liberar

Comando
./fuga

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe mala.c, que devuelve &local

Comando
printf '#include <stdio.h>\nint *mala(void) {\n  int x = 7;\n  return &x;\n}\nint main(void) {\n  int *p = mala();\n  (void)p;\n  return 0;\n}\n' > mala.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Pide a gcc los avisos de mala.c

Comando
gcc -Wall -c mala.c 2>&1 | grep -o 'Wreturn-local-addr'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un error frecuente

Advertencia

Mucha gente cree que free(p) deja p en NULL. No lo hace: p guarda la misma dirección de antes. Lo que cambia es que esa memoria ya no es tuya. Si usas p después, es un uso tras free. Escribe p = NULL tú mismo.

Sobre este laboratorio y su código: Laboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.