La mayoría de las implementaciones de las funciones de asignación de memoria C almacenarán información contable para cada bloque, ya sea en línea o por separado.
Una forma típica (en línea) es asignar realmente un encabezado y la memoria que solicitó, rellenados a un tamaño mínimo. Entonces, por ejemplo, si solicitó 20 bytes, el sistema puede asignar un bloque de 48 bytes:
- Encabezado de 16 bytes que contiene tamaño, marcador especial, suma de verificación, punteros al bloque siguiente / anterior, etc.
- Área de datos de 32 bytes (sus 20 bytes se completaron con un múltiplo de 16).
La dirección que se le proporciona es la dirección del área de datos. Luego, cuando libere el bloque, free
simplemente tomará la dirección que le dé y, suponiendo que no haya rellenado esa dirección o la memoria que la rodea, verifique la información contable inmediatamente antes. Gráficamente, eso estaría en la línea de:
____ The allocated block ____
/ \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
^
|
+-- The address you are given
Tenga en cuenta que el tamaño del encabezado y el relleno están totalmente definidos por la implementación (en realidad, todo está definido por la implementación (a), pero la opción de contabilidad en línea es común).
Las sumas de verificación y los marcadores especiales que existen en la información contable son a menudo la causa de errores como "Arena de memoria corrupta" o "Doble libre" si los sobrescribe o los libera dos veces.
El relleno (para hacer que la asignación sea más eficiente) es la razón por la que a veces puedes escribir un poco más allá del final del espacio solicitado sin causar problemas (aún así, no hagas eso, es un comportamiento indefinido y, solo porque a veces funciona, no funciona) t significa que está bien hacerlo).
(una) He escrito implementaciones de malloc
en sistemas embebidos donde obtuviste 128 bytes sin importar lo que pediste (ese era el tamaño de la estructura más grande del sistema), suponiendo que pediste 128 bytes o menos (las solicitudes de más cumplir con un valor de retorno NULL). Se usó una máscara de bits muy simple (es decir, no en línea) para decidir si se asignó un fragmento de 128 bytes o no.
Otros que he desarrollado tenían diferentes grupos para fragmentos de 16 bytes, fragmentos de 64 bytes, fragmentos de 256 bytes y fragmentos de 1K, nuevamente usando una máscara de bits para decidir qué bloques se usaron o estaban disponibles.
Ambas opciones lograron reducir la sobrecarga de la información contable y aumentar la velocidad malloc
y free
(sin necesidad de fusionar bloques adyacentes al liberar), particularmente importante en el entorno en el que estábamos trabajando.