El montón no es algo exclusivo de la arquitectura Win32 (como tampoco era la pila), sino que es un elemento que se ha utilizado en prácticamente todas las plataformas y lenguajes de programación.
Quizá a algunos de vosotros os suene la frase
"asignación dinámica de memoria". Esta frase es equivalente a decir:
"crear memoria en el montón", ya que toda asignación de memoria que se haga en ejecución, se hará en esta estructura de datos.
Su función principal es almacenar aquello que no podía ser almacenado en la pila: variables cuyo tamaño no es conocido en tiempo de compilación o variables demasiado grandes para que quepan en la pila.
Como ya vimos en el artículo anterior, para almacenar una variable, su tamaño debía ser conocido en tiempo de compilación. Esto es necesario para que el compilador vaya “colocando” cada variable en su sitio (una encima de otra en la pila), ya que si no sabe lo que ocupa una variable, no podrá saber la posición de la siguiente. Por eso sólo es posible definir variables y vectores de tamaños conocidos en tiempo de compilación.
Sin embargo, si queremos definir una zona de memoria o vector de tamaño variable, hay que actuar de otra forma.
La siguiente función crea una zona de memoria de tamaño variable en el montón (un pez de peso variable)
void CrearPez( int peso )
{
void *caña;
caña = malloc( peso );
ZeroMemory( caña, peso );
}
Como vemos, la función es muy sencilla y consta de dos pasos:
- Reservar un bloque de tamaño variable en memoria. Este bloque de memoria, reside en el montón ya que su tamaño no se conocía en tiempo de compilación. Para reservar el bloque (el pez) se utilizan una de las funciones que nos proporciona C para la asignación dinámica de memoria: malloc, la cual retorna un puntero (la caña de pescar) al bloque que ha sido reservado en el montón.
- Rellenar este bloque de memoria con ceros a través de la función del API ZeroMemory.
Sin embargo se cometen un gran error: el puntero retornado por malloc() se almacena en una variable que reside en la pila, la cual quedará descartada al finalizar la función, por lo que el "pez" que hemos creado queda libre e inaccesible. El "pez" en cuestión tendría el mismo aspecto que el que aparece en el lazo derecho de la
Figura 1: completamente perdido y sin posibilidad de ser eliminado (se nos ha escapado el muy condenado).
Este error (más común de lo que puede parecer) se denomina "goteo de memoria" o "memory leak" y desemboca en que, si esta situación se repite múltiples veces, podemos quedarnos sin memoria en el montón, ya que estaría lleno de objetos a los que no podemos acceder para eliminarlos. Estos objetos quedaría eliminados al terminar el proceso, porque la memoria de un proceso se libera completamente al terminar este.
Ahora vamos a ver el mismo código, pero con los errores corregidos:
void CrearPez( int peso )
{
void *caña;
caña = malloc( peso );
ZeroMemory( caña, peso );
free( caña );
}
Vemos que antes de terminar la función, liberamos el bloque de memoria que teníamos creado en el montón. Después de esto, ya no importa si la variable “caña” desaparece, porque el valor que contiene no es válido ni necesario.
Este es un caso típico de asignación de memoria en el montón. Como norma general, y aprovechando la vena poética, se puede decir que:
"toda variable que se crea y destruye en ejecución, reside en el montón".
Ahora vamos a ver un ejemplo un poco más complejo:
void CrearBancoPeces( int peso, int NumeroPeces )
{
void *caña;
int i;
for ( i = ; i < NumeroPeces; ++i )
{
caña = malloc( peso );
ZeroMemory( caña, peso );
}
free( caña );
}
En esta función, en vez de crearse un pez solitario, se crea un conjunto de peces. Si analizamos con cuidado, veremos que hay un "pequeño gran" bug: al dar la primera vuelta en el bucle, se crea el primer pez, y automáticamente queda pescado a través de la variable "caña". Al dar la segunda vuelta, se crea el segundo pez, y lo que hacemos es pescar con nuestra “caña” a este segundo pez. ¿y el primero? Pues ha quedado libre como el viento. Hemos dejado libre al primer pez para pescar al segundo. Así se continuará con el bucle hasta que tengamos la piscina (el montón) llena a rebosar de peces, pero sólo tendremos pescado al último. A la hora de liberar memoria, sólo liberaremos la del último elemento, ya que todos los demás no está localizados. Una vez más, hemos caído en otro "goteo de memoria".
Como ejercicio para manejo de punteros, sugiero que se realice el mismo código, pero corrigiendo el bug (una sugerencia: hace falta una caña de pescar por cada pez).