Todo el mundo diciendo 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 convencerse de esto, imagine que estaba escribiendo una aplicación grande , con varios asignadores de memoria diferentes; tal vez use raw libc malloc
en una parte, pero C++ operator new
en otra parte, y luego alguna API específica de Windows en otra parte . Entonces usted tiene todo tipo de void*
volando alrededor. Escribir una función que pueda funcionar en de estos void*
es imposible, a menos que de alguna manera pueda deducir a partir del valor del puntero de cuál de sus montones proviene.
Por lo que es posible que desee cerrar cada puntero en su programa con alguna convención que indique de dónde proviene el puntero (y dónde debe devolverse). Por ejemplo, en C++ llamamos a eso std::unique_ptr<void>
(para punteros que deben ser operator delete
'd) o std::unique_ptr<void, D>
(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 esté terminando los 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 nunca tendrá que preocuparse por el tamaño de una asignación nuevamente.
Sin embargo.
Hay también buenas razones por las que podría legítimamente quieren 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 programador pensó que estaba usando. Si cada una de sus asignaciones de 10 bytes está secretamente usando 16 bytes debajo del capó, ¡es bueno saberlo! (Por supuesto, habrá otros gastos generales también, que no está midiendo de esta manera. Pero hay otras herramientas para que 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 prematuras reasignaciones 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 de malloc libc - no de un montón de encargo, y no apunta hacia el centro de un objeto - que puede utilizar las siguientes API específicas del sistema operativo, que he agrupado en una función de contenedor "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 <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:
'sizeof (char *) ... 'es redundante, ya que' char' se garantiza que tiene un tamaño de '1'. – mk12
@ mk12 Aún deja más claro lo que está sucediendo. Especialmente cuando se escribe como 'malloc (100 * sizeof (char))', que sigue la convención usual de colocar unidades en el lado derecho de una cantidad. – DepressedDaniel
En realidad, ahora prefiero escribir 'TYPE * ptr = malloc (100 * sizeof * ptr)', donde TYPE solo se escribe una vez. Esto garantiza que obtendrá una matriz de 100 elementos, incluso si cambia TYPE. – mk12