¿Cómo sabe lo libre para liberar?
size pointers (10)
Cuando llamamos a malloc, simplemente consumimos más bytes de su requerimiento. Este consumo de más bytes contiene información como suma de cheques, tamaño y otra información adicional. Cuando llamamos gratis en ese momento, vamos directamente a esa información adicional donde se encuentra la dirección y también a la cantidad de bloque que será gratis.
En la programación en C, puedes pasar cualquier tipo de puntero que desees como argumento para liberar, ¿cómo sabe el tamaño de la memoria asignada para liberar? Siempre que pase un puntero a alguna función, también debo pasar el tamaño (es decir, una matriz de 10 elementos debe recibir 10 como parámetro para saber el tamaño de la matriz), pero no tengo que pasar el tamaño a la Función libre. ¿Por qué no, y puedo usar esta misma técnica en mis propias funciones para evitar que necesite cargar alrededor de la variable adicional de la longitud de la matriz?
De la lista de preguntas frecuentes comp.lang.c
: ¿Cómo sabe free cuántos bytes hay que liberar?
La implementación de malloc / free recuerda el tamaño de cada bloque a medida que se asigna, por lo que no es necesario recordarle el tamaño cuando se libera. (Por lo general, el tamaño se almacena adyacente al bloque asignado, razón por la cual las cosas generalmente se rompen gravemente si los límites del bloque asignado están incluso ligeramente sobrepasados)
El administrador del montón almacenó la cantidad de memoria que pertenecía al bloque asignado en algún lugar cuando llamó a malloc
.
Yo nunca implementé uno, pero creo que la memoria que está justo delante del bloque asignado puede contener la metainformación.
En una nota relacionada, la biblioteca GLib tiene funciones de asignación de memoria que no guardan el tamaño implícito, y luego simplemente pasa el parámetro de tamaño a libre. Esto puede eliminar parte de la sobrecarga.
Esta respuesta se reubica en ¿Cómo sabe free () cuánta memoria hay que desasignar? donde de repente se me impidió responder por una aparente pregunta duplicada. Esta respuesta debería ser relevante para este duplicado:
Para el caso de malloc
, el asignador de pila almacena una asignación del puntero original devuelto, a los detalles relevantes necesarios para free
la memoria más tarde. Por lo general, esto implica almacenar el tamaño de la región de memoria en cualquier forma relevante para el asignador en uso, por ejemplo, el tamaño sin procesar, o un nodo en un árbol binario utilizado para rastrear las asignaciones, o un recuento de "unidades" de memoria en uso.
free
no fallará si "cambia el nombre" del puntero o lo duplica de alguna manera. Sin embargo, no se cuenta con referencias, y solo la primera free
será correcta. S free
adicionales son errores "doble libre".
Intentar free
cualquier puntero con un valor diferente a los devueltos por malloc
s anteriores, y hasta ahora no se ha publicado es un error. No es posible liberar parcialmente las regiones de memoria devueltas desde malloc
.
La mayoría de las implementaciones de las funciones de asignación de memoria C almacenarán información de contabilidad para cada bloque, ya sea en línea o por separado.
Una forma típica (en línea) es asignar un encabezado y la memoria que solicitó, completados a un tamaño mínimo. Por ejemplo, si pidió 20 bytes, el sistema puede asignar un bloque de 48 bytes:
- Encabezado de 16 bytes que contiene tamaño, marcador especial, suma de comprobación, punteros al bloque siguiente / anterior y así sucesivamente.
- Área de datos de 32 bytes (sus 20 bytes completados a un múltiplo de 16).
La dirección que se le da a usted es la dirección del área de datos. Luego, cuando libere el bloqueo, el free
albedrío simplemente tomará la dirección que le dio y, suponiendo que no haya rellenado esa dirección o la memoria a su alrededor, 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 una común).
Las sumas de comprobación y los marcadores especiales que existen en la información contable son a menudo la causa de errores como "Arena del juego dañada" o "Doble libre" si los sobrescribe o los libera dos veces.
El relleno (para hacer que la asignación sea más eficiente) es por lo que a veces puede escribir un poco más allá del final del espacio solicitado sin causar problemas (aún así, no haga eso, es un comportamiento indefinido y, solo porque funciona a veces, no lo hace t significa que está bien hacerlo).
(a) He escrito implementaciones de malloc
en sistemas integrados donde obtuviste 128 bytes sin importar lo que pediste (ese era el tamaño de la estructura más grande del sistema), asumiendo que pediste 128 bytes o menos (solicitudes para más se cumpliría con un valor de retorno NULO). Se utilizó 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 grupos diferentes para trozos de 16 bytes, trozos de 64 bytes, trozos de 256 bytes y trozos de 1 K, 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 de malloc
y la free
(no es necesario fusionar bloques adyacentes cuando se liberan), especialmente importante en el entorno en el que trabajamos.
La técnica original consistía en asignar un bloque un poco más grande y almacenar el tamaño al principio, luego dar a la aplicación el resto del blog. El espacio adicional tiene un tamaño y posiblemente enlaces para unir los bloques libres para reutilizarlos.
Sin embargo, existen ciertos problemas con esos trucos, como el mal comportamiento de la administración de memoria y caché. El uso de la memoria en el bloque tiende a ubicar cosas innecesariamente y también crea páginas sucias que complican el uso compartido y la copia en escritura.
Así que una técnica más avanzada es mantener un directorio separado. También se han desarrollado enfoques exóticos en los que las áreas de la memoria utilizan el mismo poder de dos tamaños.
En general, la respuesta es: se asigna una estructura de datos separada para mantener el estado.
Para responder a la segunda mitad de su pregunta: sí, puede, y un patrón bastante común en C es el siguiente:
typedef struct {
size_t numElements
int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;
#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;
malloc()
y free()
dependen del sistema / compilador, por lo que es difícil dar una respuesta específica.
Más información sobre esta otra pregunta .
Cuando llama a malloc()
, especifica la cantidad de memoria para asignar. La cantidad de memoria realmente utilizada es un poco más que esto e incluye información adicional que registra (al menos) qué tan grande es el bloque. No puede (de manera confiable) acceder a esa otra información, y tampoco debería :-).
Cuando llama a free()
, simplemente mira la información adicional para averiguar qué tan grande es el bloque.