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


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

Por ejemplo, después de

char* p = malloc (100);

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

Author: Community, 2009-08-15

15 answers

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 compiladores proporcionan extensiones no estándar.) Si necesitas saberlo, tendrás que hacer un seguimiento de ello por ti mismo. (Ver también pregunta 7.28.)

 39
Author: Alex Reynolds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-12 21:05:22

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

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

 39
Author: ars,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-16 07:51:12

La mentalidad C es proporcionar al programador herramientas para ayudarlo con su trabajo, no proporcionar abstracciones que cambien la naturaleza de su trabajo. C también intenta evitar hacer las cosas 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 cero, manipulando 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 un trabajo extra, y si C lo hiciera automáticamente, a veces lo estaría haciendo innecesariamente.

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

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

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

 8
Author: Artelius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-15 12:46:02

No, la biblioteca de tiempo de ejecución C no proporciona tal función.

Algunas bibliotecas pueden proporcionar funciones específicas de la plataforma o del compilador que pueden obtener esta información, pero generalmente la forma de realizar un seguimiento de esta información es en otra variable entera.

 6
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-15 11:25:52

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 encuentras que realmente necesitas usarlas, generalmente es una señal de que lo estás haciendo mal. Usted debe almacenar el tamaño por separado, o no tiene que saber en absoluto. El uso de funciones de proveedor es la forma más rápida de perder uno de los principales beneficios de escribir en C, la portabilidad.

 3
Author: Enno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-27 21:58:08

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 de puntero seguirían funcionando como se esperaba:

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

También puedes implementar un wrapper para malloc y agregar etiquetas gratis (como tamaño asignado y otra información meta) antes del puntero devuelto por malloc. Este es, de hecho, el método que un compilador de c++ etiquetas objetos con referencias a virtual clase. Aquí hay uno trabajando ejemplo:

#include <stdlib.h>
#include <stdio.h>

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. Todo otras operaciones de puntero no requieren refactorización.

 3
Author: Leslie Godwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:17

Esperaría que esto dependiera de la implementación.
Si tiene la estructura de datos del encabezado, podría volver a colocarla en el puntero y obtener el tamaño.

 2
Author: nik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-15 11:43:08

No, no lo hay.

 1
Author: Electro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-15 11:26:22

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

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

 1
Author: Nick Dandoulakis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-15 11:36:53

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

template <class T>
int get_allocated_bytes(T* ptr)
{
 return *((int*)ptr-4);
}

template <class T>
int get_allocated_elements(T* ptr)
{
 return get_allocated_bytes(ptr)/sizeof(T);
}
 1
Author: H. Acker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-31 14:50:12

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

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

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

No hay manera de averiguar la asignación total.

 1
Author: sarin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-13 18:06:03

Bien, ahora sé que esto no responde a su pregunta específica, sin embargo, pensando fuera de la caja, por así decirlo... Se me ocurre que probablemente no necesitas saberlo. Vale, vale, no me refiero a que tengas una implementación mala o poco ortodoxa... Quiero decir es que probablemente (sin mirar su código, solo estoy adivinando) solo quiere saber si sus datos pueden caber en la memoria asignada, si ese es el caso, entonces esta solución podría ser mejor. No debe ofrecer demasiados gastos generales y resolverá su problema de "ajuste" si eso es realmente lo que está manejando:

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

O si necesita mantener el contenido antiguo:

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

Por supuesto que podrías usar:

p = realloc(p, required_size);

Y terminemos con esto.

 1
Author: Erich Horn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-12 03:13:49

Todos diciéndote que es imposible es técnicamente correcto (el mejor tipo de correcto).

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 convencerse de esto, imagine que estaba escribiendo una aplicación grande , con varios asignadores de memoria diferentes: tal vez use libc malloc raw en una parte, pero C++ operator new en otra parte, y luego alguna API de Windows específica en otra parte. Así que tienes todo tipo de void* volando alrededor. Escribir una función que pueda funcionar en cualquier de estos void*es imposible, a menos que de alguna manera pueda decir del valor del puntero de cuál de sus montones vino.

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

Sin Embargo.

Hay también buenas razones por las que usted podría legítimamente querer saber el tamaño subyacente real de una asignación. Por ejemplo, tal vez estás escribiendo una herramienta de creación de perfiles para tu aplicación que reportará la cantidad real de memoria utilizada por cada subsistema, no solo la cantidad de memoria que el programador pensó que estaba usando. Si cada una de sus asignaciones de 10 bytes está usando secretamente 16 bytes bajo el capó, ¡es bueno saberlo! (Por supuesto, también habrá otros gastos generales, que no estás midiendo de esta manera. Pero hay otras herramientas para que trabajan.) O tal vez solo estás 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 al centro de un objeto - puede usar las siguientes API específicas del sistema operativo, que he agrupado en un contenedor "portable-ish" función para mayor comodidad. Si usted encuentra un sistema común donde este código no funciona, por favor deje un comentario y voy a tratar de arreglarlo!

#if defined(__linux__)
// https://linux.die.net/man/3/malloc_usable_size
#include <malloc.h>
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 <malloc/malloc.h>
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 <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return _msize((void *)p);
}
#else
#error "oops, I don't know this system"
#endif

#include <stdio.h>
#include <stdlib.h>  // 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:

 0
Author: Quuxplusone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-04 20:29:57

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

 -1
Author: kAmol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-10 09:14:04

No estoy seguro pero intenta:

char **q = &p;
int size = q[1] - q[0]; 
 -4
Author: manish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-13 18:06:27