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
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Pila | la memoria de las variables locales de cada función | |
| Marco de pila | el bloque de la pila que usa una llamada | |
| Montón | la memoria que pides y devuelves tú, también llamada heap | |
| malloc | pide al montón un bloque de bytes | malloc(20) |
| free | devuelve un bloque al montón | free(p) |
| Fuga de memoria | un 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
| Momento | Marcos en la pila | Qué vale local |
|---|---|---|
| Empieza main | main | no existe |
| main llama a cuenta(3) | main, cuenta | 6 |
| cuenta devuelve 6 | main | el marco se ha ido |
| main llama a cuenta(10) | main, cuenta | 20, en un marco nuevo |
| cuenta devuelve 20 | main | el marco se ha ido |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Entra en la carpeta de trabajo
mkdir -p ~/c && cd ~/cSe 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
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.cSe 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
gcc -Wall -o pila pila.cSe 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
./pilaSe 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
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.cSe 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
gcc -Wall -o mon mon.cSe 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
./monSe 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
# 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
| Error | Qué ocurre | Cómo se evita |
|---|---|---|
| Fuga de memoria | El programa gasta memoria sin parar | Un free por cada malloc |
| Uso tras free | Lee o escribe memoria que ya no es suya | Poner p = NULL tras free(p) |
| Doble free | El montón se corrompe | Poner p = NULL tras free(p) |
| Devolver &local | Apunta a un marco que ya no existe | Devolver 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
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.cSe 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
gcc -Wall -o fuga fuga.cSe 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
./fugaSe 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
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.cSe 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
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.