Ya hemos visto como se crea un montón, y se reserva un bloque en él para ser utilizado posteriormente a través del puntero que nos retorna. Puede ser que, en cierto momento, el tamaño que inicialmente hemos asignado, se quede demasiado pequeño, por lo cual debemos ampliar este bloque. A esta operación se le denomina "reasignación".
La re-asignación es una operación delicada, ya que fragmenta la memoria del montón con mucha facilidad (ver más abajo cuando hablo sobre la
implementación∞). Como todos podréis imaginaros, no es lo mismo reservar un bloque inicial de 2 KB e ir re-asignándolo varias veces, hasta que pueda albergar 100 KB, que reservar directamente un bloque inicial de 100 KB.
Para evitar esta situación, y como norma general, una de las primeras recomendaciones que se hace durante el primer curso de programación, es que siempre se reasigne un valor que sea el doble del tamaño actual del bloque. Es decir: si inicialmente hemos reservado un bloque de 16 KB, a la hora de re-asignarlo, debemos hacerlo de 32 KB, aunque en realidad con 17 KB nos sea suficiente. De este modo, conseguimos mantener controlada la fragmentación del montón, y nunca desperdiciaremos más del 50% del total de la memoria (esto es demostrable matemáticamente).
Para ello existe la función HeapReAlloc. Esta función, además de permitirnos cambiar el tamaño de un montón, nos ofrece la posibilidad de cambiar las opciones de creación.
El definición de la función es la siguiente:
LPVOID HeapReAlloc(
HANDLE hMonton,
descriptor del montón
DWORD flOpciones, banderas de re-asignación
LPVOID lpBloque,
puntero al bloque
DWORD dwTamaño); nuevo tamaño del bloque
Los parámetros tienen los siguientes significados:
- hMonton: indica el descriptor del montón donde queremos realizar la re-asignación de memoria. Este descriptor se consigue en la llamada a HeapCreate. Más adelante hablamos con detalle cómo conseguir un descriptor de montón del proceso.
- flOpciones: indica las nuevas banderas que establecerán al montón. Estas banderas, sobrescriben a las que se indicaron durante la creación con HeapCreate:
- HEAP_GENERATE_EXCEPTIONS: igual que en HeapCreate.
- HEAP_NO_SERIALIZE: igual que en HeapCreate.
- HEAP_ZERO_MEMORY: Si se amplia el bloque, los bytes adicionales se rellenarán con ceros.
- HEAP_REALLOC_INPLACE_ONLY: no permite que se mueva el bloque completo cuando se amplía su tamaño. Si no hay bloques contiguos libres para poder ampliar el espacio, la función fallará. Esta bandera es útil cuando dentro de este bloque de memoria hay elementos que están apuntados por distintas variables punteros. Si se permitiese la recolocación del bloque completo, el valor de dichos punteros ya no sería el correcto, por lo que ya no podríamos acceder a esos elementos apuntados.
- lpBloque: es un puntero al inicio del bloque que queremos re-asignar. Normalmente, este valor proviene de una llamada previa a HeapAlloc.
- dwTamaño: Nuevo número de bytes que ocupará el montón. Este tamaño puede ser mayor, menor o igual al tamaño indicado en la creación., sin embargo, si al crear el montón se indicó un tamaño máximo (dwTamañoMaximo > 0), este parámetro tiene que ser menor que 524.280.
Esta función retornará un puntero a la zona de memoria donde comienza el bloque re-asignado. Este puntero, será igual que el parámetro lpBloque si se utilizó la bandera HEAP_REALLOC_INPLACE_ONLY o puede ser completamente distinto si ha necesario resituar el bloque para ampliarlo.