Si free () conoce la longitud de mi matriz, ¿por qué no puedo solicitarla en mi propio código?

Sé que es una convención común pasar la longitud de los arreglos asignados dinámicamente a las funciones que los manipulan:

void initializeAndFree(int* anArray, size_t length); int main(){ size_t arrayLength = 0; scanf("%d", &arrayLength); int* myArray = (int*)malloc(sizeof(int)*arrayLength); initializeAndFree(myArray, arrayLength); } void initializeAndFree(int* anArray, size_t length){ int i = 0; for (i = 0; i < length; i++) { anArray[i] = 0; } free(anArray); } 

pero si no tengo forma de obtener la longitud de la memoria asignada desde un puntero, ¿cómo sabe free() automágicamente free() qué se debe desasignar cuando todo lo que le doy es el mismo puntero? ¿Por qué no puedo entrar en la magia, como progtwigdor en C?

¿De dónde obtiene free () su conocimiento libre (har-har)?

Además del punto correcto de Klatchko que el estándar no contempla, las implementaciones reales de malloc / free a menudo asignan más espacio de lo que se solicita. Por ejemplo, si solicita 12 bytes, puede proporcionar 16 (consulte Un asignador de memoria , que señala que 16 es un tamaño común). Por lo tanto, no es necesario saber que usted solicitó 12 bytes, solo que le dio una porción de 16 bytes.

No se puede obtener porque el comité C no lo exigió en la norma.

Si está dispuesto a escribir algún código no portátil, puede tener suerte con:

 *((size_t *)ptr - 1) 

o tal vez:

 *((size_t *)ptr - 2) 

Pero si eso funcionará dependerá exactamente de dónde la tienda de malloc que está utilizando almacena esos datos.

Si bien es posible obtener los metadatos que el asignador de memoria coloca antes del bloque asignado, esto solo funcionaría si el puntero es realmente un puntero a un bloque asignado dinámicamente. Esto afectaría seriamente la utilidad de la función que requiere que todos los argumentos pasados ​​fueran punteros a tales bloques en lugar de decir una simple matriz automática o estática.

El punto es que no hay una forma portátil desde la inspección del puntero para saber a qué tipo de memoria apunta. Entonces, si bien es una idea interesante, no es una propuesta particularmente segura.

Un método seguro y portátil sería reservar la primera palabra de la asignación para mantener la longitud. GCC (y quizás algunos otros comstackdores) admite un método no portátil de implementación utilizando una estructura con una matriz de longitud cero que simplifica un poco el código en comparación con una solución portátil:

 typedef tSizedAlloc { size_t length ; char* alloc[0] ; // Compiler specific extension!!! } ; // Allocating a sized block tSizedAlloc* blk = malloc( sizeof(tSizedAlloc) + length ) ; blk->length = length ; // Accessing the size and data information of the block size_t blk_length = blk->length ; char* data = blk->alloc ; 

Después de leer la respuesta de Klatchko , yo mismo lo probé y ptr[-1] hecho almacena la memoria real (por lo general, más de la memoria que solicitamos probablemente para guardar contra la falla de segmentación).

 { char *a = malloc(1); printf("%u\n", ((size_t *)a)[-1]); //prints 17 free(a); exit(0); } 

Al intentar con diferentes tamaños, GCC asigna la memoria de la siguiente manera:

Inicialmente la memoria asignada es de 17 bytes.
La memoria asignada es de al menos 5 bytes más que el tamaño solicitado, si se solicita más, asigna 8 bytes más.

  • Si el tamaño es [0,12], la memoria asignada es 17.
  • Si el tamaño es [13], la memoria asignada es 25.
  • Si el tamaño es [20], la memoria asignada es 25.
  • Si el tamaño es [21], la memoria asignada es 33.

Sé que este hilo es un poco viejo, pero todavía tengo algo que decir. Hay una función (o una macro, todavía no he comprobado la biblioteca) malloc_usable_size (): obtiene el tamaño del bloque de memoria asignado del montón. La página de manual indica que es solo para la depuración, ya que no genera el número solicitado sino el número que asignó, que es un poco más grande. Note que es una extensión de GNU.

Por otro lado, puede que ni siquiera sea necesario, porque creo que para liberar parte de la memoria no es necesario saber su tamaño. Simplemente quite el manejador / descriptor / estructura que se encarga de la parte.

Una forma no estándar es utilizar _msize() . El uso de esta función hará que su código no sea portátil. Además, la documentación no es muy clara, por lo que devolverá el número transferido a malloc() o el tamaño real del bloque (podría ser mayor).

malloc implementador de malloc cómo almacenar estos datos. La mayoría de las veces, la longitud se almacena directamente delante de la memoria asignada (es decir, si desea asignar 7 bytes, se asignan 7 + x bytes en realidad donde se utilizan los x bytes adicionales para almacenar los metadatos). A veces, los metadatos se almacenan antes y después de la memoria asignada para verificar si hay daños en el montón. Pero el implementador también puede optar por utilizar una estructura de datos adicional para almacenar los metadatos.

Puede asignar más memoria para almacenar tamaño:

 void my_malloc(size_t n,size_t size ) { void *p = malloc( (n * size) + sizeof(size_t) ); if( p == NULL ) return NULL; *( (size_t*)p) = n; return (char*)p + sizeof(size_t); } void my_free(void *p) { free( (char*)p - sizeof(size_t) ); } void my_realloc(void *oldp,size_t new_size) { ... } int main(void) { char *p = my_malloc( 20, 1 ); printf("%lu\n",(long int) ((size_t*)p)[-1] ); return 0; } 

Para responder a la pregunta sobre eliminar [], las primeras versiones de C ++ realmente requerían que llamara a eliminar [n] y le dijera al tiempo de ejecución el tamaño, de modo que no tuviera que almacenarlo. Lamentablemente, este comportamiento fue eliminado como “demasiado confuso”.

(Vea D&E para más detalles).