Determine el tamaño de la memoria asignada dinámicamente en C

¿Hay alguna forma en C para averiguar el tamaño de la memoria asignada dinámicamente?

Por ejemplo, después

char* p = malloc (100); 

¿Hay alguna manera de averiguar el tamaño de la memoria asociada con p ?

comp.lang.c Lista de preguntas frecuentes · Pregunta 7.27 –

P. Entonces, ¿puedo consultar el paquete malloc para averiguar qué tan grande es un bloque asignado?

R. Desafortunadamente, no hay una forma estándar o portátil. (Algunos comstackdores proporcionan extensiones no estándar). Si necesita saberlo, tendrá que hacer un seguimiento de ello usted mismo. (Véase también la pregunta 7.28 .)

No hay una forma estándar de encontrar esta información. Sin embargo, algunas implementaciones proporcionan funciones como msize para hacer esto. Por ejemplo:

  • _msize en Windows
  • malloc_size en MacOS
  • malloc_usable_size en sistemas con glibc

Sin embargo, tenga en cuenta que malloc asignará un mínimo del tamaño solicitado, por lo que debe comprobar si la variante msize para su implementación realmente devuelve el tamaño del objeto o la memoria realmente asignada en el montón.

La mentalidad C es proporcionar al progtwigdor herramientas para ayudarlo con su trabajo, no para proporcionar abstracciones que cambien la naturaleza de su trabajo. C también trata de evitar que las cosas sean más fáciles / seguras si esto sucede a expensas del límite de rendimiento.

Ciertas cosas que le gustaría hacer con una región de memoria solo requieren la ubicación del inicio de la región. Tales cosas incluyen trabajar con cadenas terminadas en nulo, manipular los primeros n bytes de la región (si se sabe que la región es al menos así de grande), y así sucesivamente.

Básicamente, hacer un seguimiento de la longitud de una región es trabajo adicional, y si C lo hiciera automáticamente, a veces lo haría innecesariamente.

Muchas funciones de la biblioteca (por ejemplo, fread() ) requieren un puntero al comienzo de una región, y también el tamaño de esta región. Si necesita el tamaño de una región, debe hacer un seguimiento de ello.

Sí, las implementaciones de malloc () generalmente hacen un seguimiento del tamaño de una región, pero pueden hacerlo indirectamente o redondearlo a algún valor o no mantenerlo en absoluto. Incluso si lo admiten, encontrar el tamaño de esta manera puede ser lento en comparación con el seguimiento de usted mismo.

Si necesita una estructura de datos que sepa cuán grande es cada región, C puede hacer eso por usted. Simplemente use una estructura que haga un seguimiento de qué tan grande es la región, así como un puntero a la región.

No, la biblioteca C runtime no proporciona esa función.

Algunas bibliotecas pueden proporcionar funciones específicas de plataforma o comstackdor que pueden obtener esta información, pero en general la forma de hacer un seguimiento de esta información se encuentra en otra variable entera.

Como todos los demás ya dijeron: No, no hay.

Además, siempre evitaría todas las funciones específicas del proveedor aquí, porque cuando encuentre que realmente necesita usarlas, eso generalmente es una señal de que lo está haciendo mal. Debe almacenar el tamaño por separado, o no tener que saberlo en absoluto. Usar las funciones del proveedor es la forma más rápida de perder uno de los principales beneficios de escribir en C, la portabilidad.

Esperaría que esto sea dependiente de la implementación.
Si obtuviste la estructura de datos del encabezado, podrías devolverla al puntero y obtener el tamaño.

Este código probablemente funcione en la mayoría de las instalaciones de Windows:

 template  int get_allocated_bytes(T* ptr) { return *((int*)ptr-4); } template  int get_allocated_elements(T* ptr) { return get_allocated_bytes(ptr)/sizeof(T); } 

Esta es la mejor manera que he visto para crear un puntero etiquetado para almacenar el tamaño con la dirección. Todas las funciones del puntero seguirían funcionando como se esperaba:

Robado de: https://stackoverflow.com/a/35326444/638848

También podría implementar un contenedor para malloc y agregar tags gratis (como el tamaño asignado y otra metainformación) antes de que Malloc devuelva el puntero. Este es, de hecho, el método que un comstackdor de C ++ etiqueta objetos con referencias a clases virtuales. Aquí hay un ejemplo de trabajo:

 #include  #include  void * my_malloc(size_t s) { size_t * ret = malloc(sizeof(size_t) + s); *ret = s; return &ret[1]; } void my_free(void * ptr) { free( (size_t*)ptr - 1); } size_t allocated_size(void * ptr) { return ((size_t*)ptr)[-1]; } int main(int argc, const char ** argv) { int * array = my_malloc(sizeof(int) * 3); printf("%u\n", allocated_size(array)); my_free(array); return 0; } 

La ventaja de este método sobre una estructura con tamaño y puntero

  struct pointer { size_t size; void *p; }; 

es que solo necesita reemplazar el malloc y las llamadas gratuitas. Todas las demás operaciones de puntero no requieren refactorización.

No, no hay.

Si usa malloc, entonces no puede obtener el tamaño.

Por otro lado, si utiliza la API del sistema operativo para asignar dinámicamente la memoria, como las funciones del montón de Windows, entonces es posible hacer eso.

Esto puede funcionar, una pequeña actualización en tu código:

 void* inc = (void*) (++p) size=p-inc; 

Pero esto dará como resultado 1, es decir, memoria asociada con p si es char* . Si es int* , el resultado será 4.

No hay forma de averiguar la asignación total.

Bueno, ahora sé que esto no responde a tu pregunta específica, sin embargo, pensar fuera de la caja, por así decirlo … Se me ocurre que probablemente no necesites saberlo. Ok, vale, no, no me refiero a que tengas una implementación mala o poco ortodoxa … quiero decir es que probablemente (sin mirar tu código solo estoy adivinando) probablemente quieras saber si tus datos pueden caber en la memoria asignada, si ese es el caso, entonces esta solución podría ser mejor. No debería ofrecer demasiada sobrecarga y resolverá su problema de “ajuste” si eso es realmente lo que está manipulando:

 if ( p != (tmp = realloc(p, required_size)) ) p = tmp; 

o si necesita mantener los contenidos anteriores:

 if ( p != (tmp = realloc(p, required_size)) ) memcpy(tmp, p = tmp, required_size); 

por supuesto, podrías usar:

 p = realloc(p, required_size); 

y termine con esto.

Todo el mundo diciéndole que es imposible es técnicamente correcto (el mejor tipo de corrección).

Por razones de ingeniería, es una mala idea confiar en el subsistema malloc para decirle con precisión el tamaño de un bloque asignado. Para convencerte de esto, imagina que estás escribiendo una aplicación grande , con varios asignadores de memoria diferentes; tal vez uses raw libc malloc en una parte, pero operator new C ++ operator new en otra parte, y luego alguna API específica de Windows en otra parte. Así que tienes todo tipo de void* volando. Escribir una función que pueda funcionar en cualquiera de estos void* es imposible, a menos que de algún modo pueda determinar a partir del valor del puntero de cuál de sus montones proviene.

Así que es posible que desee envolver cada puntero en su progtwig con alguna convención que indique de dónde proviene el puntero (y dónde debe devolverse). Por ejemplo, en C ++ llamamos a std::unique_ptr (para punteros que necesitan ser operator delete ‘d) o std::unique_ptr (para punteros que deben devolverse a través de algún otro mecanismo D ) Podrías hacer el mismo tipo de cosas en C si quisieras. Y una vez que termine con los punteros en objetos más grandes y seguros, es solo un pequeño paso para struct SizedPtr { void *ptr; size_t size; } struct SizedPtr { void *ptr; size_t size; } struct SizedPtr { void *ptr; size_t size; } y luego nunca más tendrá que preocuparse por el tamaño de una asignación.

Sin embargo.

También hay buenas razones por las que legítimamente podría querer saber el tamaño subyacente real de una asignación. Por ejemplo, tal vez esté escribiendo una herramienta de creación de perfiles para su aplicación que informará la cantidad real de memoria utilizada por cada subsistema, no solo la cantidad de memoria que el progtwigdor pensó que estaba utilizando. Si cada una de sus asignaciones de 10 bytes está secretamente usando 16 bytes debajo del capó, ¡es bueno saberlo! (Por supuesto que habrá otros gastos generales también, que no está midiendo de esta manera. Pero hay otras herramientas para ese trabajo). O tal vez solo está investigando el comportamiento de realloc en su plataforma. O tal vez le gustaría “redondear” la capacidad de una asignación creciente para evitar reasignaciones prematuras en el futuro. Ejemplo:

 SizedPtr round_up(void *p) { size_t sz = portable_ish_malloced_size(p); void *q = realloc(p, sz); // for sanitizer-cleanliness assert(q != NULL && portable_ish_malloced_size(q) == sz); return (SizedPtr){q, sz}; } bool reserve(VectorOfChar *v, size_t newcap) { if (v->sizedptr.size >= newcap) return true; char *newdata = realloc(v->sizedptr.ptr, newcap); if (newdata == NULL) return false; v->sizedptr = round_up(newdata); return true; } 

Para obtener el tamaño de la asignación detrás de un puntero no nulo que ha sido devuelto directamente desde libc malloc , no desde un montón personalizado, y no apuntando hacia el centro de un objeto, puede usar las siguientes API específicas del sistema operativo, que se han agrupado en una función de envoltura “portable-ish” para mayor comodidad. Si encuentra un sistema común donde este código no funciona, ¡deje un comentario y trataré de solucionarlo!

 #if defined(__linux__) // https://linux.die.net/man/3/malloc_usable_size #include  size_t portable_ish_malloced_size(const void *p) { return malloc_usable_size((void*)p); } #elif defined(__APPLE__) // https://www.unix.com/man-page/osx/3/malloc_size/ #include  size_t portable_ish_malloced_size(const void *p) { return malloc_size(p); } #elif defined(_WIN32) // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize #include  size_t portable_ish_malloced_size(const void *p) { return _msize((void *)p); } #else #error "oops, I don't know this system" #endif #include  #include  // for malloc itself int main() { void *p = malloc(42); size_t true_length = portable_ish_malloced_size(p); printf("%zu\n", true_length); } 

Probado en:

  • Visual Studio, Win64 – _msize
  • GCC / Clang, glibc, Linux – malloc_usable_size
  • Clang, libc, Mac OS X – malloc_size
  • Clang, jemalloc, Mac OS X: funciona en la práctica pero no me fiaría (mezcla silenciosamente malloc de jemalloc y malloc_size de la libc malloc_size )
  • Debería funcionar bien con jemalloc en Linux
  • Debería funcionar bien con dlmalloc en Linux si se comstack sin USE_DL_PREFIX
  • Debería funcionar bien con tcmalloc en todas partes

int *a; a=malloc(n*sizeof(int)); si malloc devuelve NULL, significa que no obtuvo la memoria; de lo contrario, obtendrá la dirección base del bloque asignado, es decir, el tamaño de bloque (n*sizeof(int)) .

No estoy seguro, pero intenta:

 char **q = &p; int size = q[1] - q[0];