¿Cómo puedo obtener el tamaño de una matriz a partir de un puntero en C?
He asignado una "matriz" de mystruct
tamaño n
como 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?
No, no hay forma de obtener esta información sin depender en gran medida de los detalles de implementación de malloc
. En particular, malloc
puede 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 n
explí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 n
en 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;
n
ahora 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 arrInfo
mensajes 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.
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ó.
Algunos compiladores proporcionan msize() o funciones similares (_msize(), etc.), que le permiten hacer exactamente eso