¿Cómo puedo obtener el tamaño de una matriz a partir de un puntero en C?

Resuelto Joel asked hace 16 años • 16 respuestas

He asignado una "matriz" de mystructtamaño ncomo esta:

if (NULL == (p = calloc(sizeof(struct mystruct) * n,1))) {
 /* handle error */
}

Más adelante solo tengo acceso a p, y ya no tengo n. ¿Hay alguna manera de determinar la longitud de la matriz con solo el puntero?p ?

Me imagino que debe ser posible, ya que free(p)hace precisamente eso. Sé que malloc()realiza un seguimiento de cuánta memoria ha asignado y por eso conoce la longitud; ¿Quizás haya una manera de consultar esta información? Algo como...

int length = askMallocLibraryHowMuchMemoryWasAlloced(p) / sizeof(mystruct)

Sé que debería reelaborar el código para saberlo n, pero preferiría no hacerlo si es posible. ¿Algunas ideas?

Joel avatar Oct 24 '08 14:10 Joel
Aceptado

No, no hay forma de obtener esta información sin depender en gran medida de los detalles de implementación de malloc. En particular, mallocpuede asignar más bytes de los que solicita (por ejemplo, para lograr eficiencia en una arquitectura de memoria particular). Sería mucho mejor rediseñar su código para realizar un seguimiento nexplícito. La alternativa es al menos el mismo rediseño y un enfoque mucho más peligroso (dado que no es estándar, abusa de la semántica de los punteros y será una pesadilla de mantenimiento para aquellos que vengan después de usted): almacene la longitud nen el malloc'd dirección, seguida de la matriz. La asignación sería entonces:

void *p = calloc(sizeof(struct mystruct) * n + sizeof(unsigned long int),1));
*((unsigned long int*)p) = n;

nahora está almacenado en *((unsigned long int*)p)y el inicio de su matriz ahora es

void *arr = p+sizeof(unsigned long int);

Editar: Solo para hacer de abogado del diablo... Sé que todas estas "soluciones" requieren rediseños, pero vamos a hacerlo. Por supuesto, la solución presentada anteriormente es solo una implementación hacky de una estructura (bien empaquetada). También podrías definir:

typedef struct { 
  unsigned int n;
  void *arr;
} arrInfo;

y pasar arrInfomensajes en lugar de punteros sin formato.

Ahora estamos cocinando. Pero mientras estés rediseñando, ¿por qué detenerte aquí? Lo que realmente quieres es un tipo de datos abstracto (ADT). Cualquier texto introductorio para una clase de algoritmos y estructuras de datos sería suficiente. Un ADT define la interfaz pública de un tipo de datos pero oculta la implementación de ese tipo de datos. Por lo tanto, públicamente un ADT para una matriz podría verse así

typedef void* arrayInfo;
(arrayInfo)newArrayInfo(unsignd int n, unsigned int itemSize);
(void)deleteArrayInfo(arrayInfo);
(unsigned int)arrayLength(arrayInfo);
(void*)arrayPtr(arrayInfo);
...

En otras palabras, un ADT es una forma de encapsulación de datos y comportamiento... en otras palabras, es lo más parecido a la programación orientada a objetos que se puede llegar usando C puro. A menos que esté atrapado en una plataforma que no Si tienes un compilador de C++, también podrías hacer todo lo posible y simplemente usar un STL std::vector.

Allí, respondimos una pregunta simple sobre C y terminamos en C++. Dios nos ayuda a todos.

Barry Wark avatar Oct 24 '2008 07:10 Barry Wark

Solo para confirmar las respuestas anteriores: no hay forma de saber, simplemente estudiando un puntero, cuánta memoria asignó un malloc que devolvió este puntero.

¿Y si funcionara?

Un ejemplo de por qué esto no es posible. Imaginemos el código con una función hipotética llamada get_size(void *) que devuelve la memoria asignada para un puntero:

typedef struct MyStructTag
{ /* etc. */ } MyStruct ;

void doSomething(MyStruct * p)
{
   /* well... extract the memory allocated? */
   size_t i = get_size(p) ;
   initializeMyStructArray(p, i) ;
}

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   doSomething(s) ;
}

¿Por qué incluso si funcionara, no funcionaría de todos modos?

Pero el problema de este enfoque es que, en C, puedes jugar con la aritmética de punteros. Reescribamos hacerAlgoMás():

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   MyStruct * s2 = s + 5 ; /* s2 points to the 5th item */
   doSomething(s2) ; /* Oops */
}

Cómo se supone que funciona get_size, ya que envió a la función un puntero válido, pero no el devuelto por malloc. E incluso si get_size se tomara todas las molestias para encontrar el tamaño (es decir, de manera ineficiente), devolvería, en este caso, un valor que sería incorrecto en su contexto.

Conclusión

Siempre hay maneras de evitar este problema, y ​​en C, siempre puedes escribir tu propio asignador, pero nuevamente, quizás sea demasiado problemático cuando todo lo que necesitas es recordar cuánta memoria se asignó.

paercebal avatar Oct 24 '2008 07:10 paercebal

Algunos compiladores proporcionan msize() o funciones similares (_msize(), etc.), que le permiten hacer exactamente eso

dmityugov avatar Oct 24 '2008 09:10 dmityugov