El heap (montículo) y la pila de llamadas (stack) son estructuras de datos distintas pero, a veces, puede llegar a ser complicado identificar sus diferencias, principalmente cuando recién aprendes programación. En esta entrada te explicaré de forma breve las diferencia entre ambas estructuras de datos.
La pila de llamadas (call stack)
La pila de llamadas, pila de ejecución, pila de función, pila de control, pila de tiempo de ejecución o simplemente call stack es una estructura dinámica de datos que almacena la información sobre las subrutinas activas de un programa en ejecución. Cuando hacemos una llamada a una función, un bloque en el tope de la pila es reservado para guardar las variables locales junto con algunos datos necesarios para garantizar el funcionamiento adecuado de la estructura (como la dirección a la que tendrá que retornar el hilo de ejecución cuando termine la función). Después de retornar, el bloque de la pila que ocupaba el llamado, se libera para poder utilizarse más adelante de ser necesario.
Se llama pila de ejecución porque es una estructura de datos que funciona bajo el concepto de LIFO (last in first out). Esto hace que sea más sencillo mantener el control de los bloques que deben ser liberados, puesto que será aquel que esté en el tope de la pila.
Pueden existir múltiples pilas de ejecución en un programa cuando este es multihilo; cada hilo tiene su propia pila con la que mantendrá el control de sus llamadas a funciones.
El heap (almacenamiento libre)
El montículo libre, zona libre, almacenamiento libre o heap es una estructura dinámica de datos utilizada para almacenar datos en ejecución. A diferencia de la pila de ejecución que solamente almacena las variables declaradas en los bloques previo a su ejecución, el heap permite reservar memoria dinámicamente, es decir, es el encargado de que la «magia» de la memoria dinámica ocurra. Las variables globales y estáticas también son almacenadas en él.
Como esta estructura de datos no sigue ninguna metodología, es un poco complicado mantener el control de los bloques de memoria reservados puesto que se pueden ocupar y liberar espacios en cualquier momento.
Ejemplo de heap vs stack
Si compilas y ejecutas el siguiente ejemplo, podrás tener una idea general de cómo se comportan la pila y el heap:
#include <stdio.h> #include <stdlib.h> void foo(int valor) { unsigned char c; unsigned char *ptr = malloc(1); printf("Stack: %p | Heap: %p\n", &c, ptr); if(valor <= 0) return; foo(valor - 1); } int main(){ foo(10); return 0; }
Ejemplo de salida:
Explicación: La variable c nos permite mostrar la pila porque es declarada como variable interna en cada recursión de foo(). El apuntador *ptr nos permite visualizar el heap porque utilizamos malloc() para reservar memoria dinámicamente durante la ejecución del programa. Que las direcciones de memoria incrementen o disminuyan depende de la arquitectura del equipo con el que trabajes.
Como pudiste darte cuenta, la pila de ejecución y el heap son estructuras que se usan para almacenar datos durante la ejecución de nuestros programas, sin embargo, sus diferencias son muy marcadas y es necesario aprender a identificarlas para administrar mejor la memoria de nuestros equipos. Si te interesa leer más al respecto de apuntadores y de memoria dinámica, te recomiendo leer la serie de apuntadores que ponemos a tu alcance en este enlace.
Muy buen aporte, me ayudo para estudiar Sistemas Operativos. Muchas gracias!
Buen resumen.
Hay un memory leak, cuando valor <= 0 no se hace free() de ptr.