← Gestión de Memoria Bloque 2 · La pila y el heap · Tema 10 de 52

Tema 10

Simulador de pila y heap

En este tema unimos todo lo visto sobre la pila y el heap en un único simulador: un programa de ejemplo se ejecuta paso a paso mientras se dibujan, en paralelo, los marcos de la pila y los bloques del heap. Al terminar podrás predecir, para cualquier programa en C, cómo crece la pila con las llamadas y cómo se asignan y liberan los bloques del heap.

1. Qué vamos a simular

El simulador ejecuta un programa de ejemplo en pasos discretos. En cada paso ocurre una de estas operaciones:

En paralelo se dibujan la pila (marcos apilados) y el heap (bloques asignados/libres). El usuario controla el avance con botones.

2. El programa de ejemplo

Este es el programa que simula. Es deliberadamente simple para que cada operación sea visible:

ejemplo.c
#include <stdio.h>
#include <stdlib.h>

void procesar(int x)
{
    int local = x * 2;          /* marco de procesar */
    char *buf = malloc(16);      /* bloque en el heap */
    buf[0] = (char)local;
    printf("%d\\n", local);
    free(buf);                   /* liberar bloque */
}                                /* destruir marco */

int main(void)
{
    int i;
    for (i = 0; i < 3; i++) {
        procesar(i);             /* llamada recursiva no recursiva */
    }
    char *g = malloc(32);        /* bloque que no se libera */
    g[0] = 1;
    return 0;                   /* fuga de memoria */
}

El programa llama a procesar tres veces (cada una crea un marco y un bloque del heap que se libera) y al final pide un bloque de 32 bytes que nunca se libera (fuga).

3. Cómo se dibuja la pila

La pila se dibuja como una columna de marcos, de arriba (direcciones altas) hacia abajo (direcciones bajas). El marco actual está en la parte inferior. Cada marco muestra:

Al entrar a una función se añade un marco; al salir se quita el último.

4. Cómo se dibuja el heap

El heap se dibuja como una fila de bloques. Cada bloque muestra:

Al hacer malloc se añade un bloque asignado; al hacer free se marca como libre (reutilizable).

5. El simulador interactivo

Usa los botones para avanzar paso a paso. El panel izquierdo muestra el código con la línea actual resaltada; el derecho muestra la pila y el heap.

Simulador · pila y heap paso a paso

CÓDIGO


          

PILA (crece hacia abajo)

HEAP

6. Qué observar

7. Limitaciones del simulador

Este simulador es una simplificación didáctica. No modela:

Para ver la pila y el heap reales, usa GDB y Valgrind (siguiente sección).

8. Mirar en Linux: gdb y valgrind

terminal · inspeccionar pila y heap reales
$ gcc -g -O0 -o ejemplo ejemplo.c
$ gdb ./ejemplo
(gdb) break procesar
(gdb) run
(gdb) bt               # backtrace: marcos de activación
(gdb) info locals      # variables locales del marco actual
(gdb) print buf        # valor del puntero
(gdb) x/16bx buf       # contenido del bloque del heap
$ valgrind --leak-check=full ./ejemplo
==...== HEAP SUMMARY:
==...==     in use at exit: 32 bytes in 1 blocks
==...==   total heap usage: 4 allocs, 3 frees, 80 bytes allocated
==...== LEAK SUMMARY:
==...==    definitely lost: 32 bytes in 1 blocks==...==    definitely lost: 32 bytes in 1 blocks

9. Resumen

10. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué se crea en la pila al llamar a una función?

Un marco de activación con la dirección de retorno, los parámetros, las variables locales y los registros salvados.

2. ¿Qué se crea en el heap al llamar a malloc?

Un bloque de memoria dinámica del tamaño pedido, precedido por metadatos del asignador.

3. ¿Qué ocurre con la pila al retornar de una función?

El marco se destruye y el puntero de pila se restaura, reutilizando el espacio.

4. ¿Qué ocurre con el heap al llamar a free?

El bloque se marca como libre y pasa a la lista de reutilización del asignador.

5. ¿Por qué el programa de ejemplo tiene una fuga?

Porque el bloque de 32 bytes pedido al final (g = malloc(32)) nunca se libera con free.

6. ¿Qué muestra gdb con bt?

El backtrace: la lista de marcos de activación desde la función actual hasta main.

7. ¿Qué muestra Valgrind al final de la ejecución?

El resumen del heap: bytes en uso, número de asignaciones y liberaciones, y fugas detectadas.

8. ¿Qué limitación tiene el simulador de este tema?

No modela los metadatos del asignador, la reutilización real de bloques ni la concurrencia.

Para practicar

  1. Ejecuta el programa de ejemplo con GDB y observa cómo crece la pila en cada llamada a procesar.
  2. Modifica el programa para liberar el bloque final y verifica con Valgrind que no hay fugas.
  3. Añade una variable local grande en procesar y observa cómo cambia el tamaño del marco.
  4. Usa x/16bx buf en GDB para ver el contenido del bloque antes y después de escribir.
  5. Compila con -fno-omit-frame-pointer y observa cómo cambia el backtrace en GDB.
  6. Modifica el simulador para añadir una operación de realloc y observa el efecto.
  7. Compara el número de allocs y frees reportados por Valgrind con los del programa.