Pregunta

He asignado una " matriz " de mystruct de tamaño n de esta manera:

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 solo con el puntero p ?

Me imagino que debe ser posible, ya que free (p) hace exactamente eso. Sé que malloc () hace un seguimiento de la cantidad de memoria que ha asignado, y es por eso que conoce la longitud; Tal vez hay una manera de consultar esta información? Algo así como ...

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

Sé que debería volver a trabajar el código para que sepa n , pero preferiría no hacerlo si es posible. ¿Alguna idea?

¿Fue útil?

Solución

No, no hay forma de obtener esta información sin depender mucho de los detalles de implementación de malloc . En particular, malloc puede asignar más bytes de los que solicita (por ejemplo, eficiencia en una arquitectura de memoria particular). Sería mucho mejor rediseñar su código para que pueda realizar un seguimiento de n explícitamente. La alternativa es al menos tanto un rediseño como 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 vienen después de usted): almacenar la longitud n en la dirección malloc'd, 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 se almacena en * ((unsigned long int *) p) y el inicio de su matriz es ahora

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

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

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

y pase alrededor de arrInfo en lugar de punteros en bruto.

Ahora estamos cocinando. Pero mientras estés rediseñando, ¿por qué parar aquí? Lo que realmente quieres es un tipo de datos abstracto (ADT). Cualquier texto introductorio para una clase de algoritmos y estructuras de datos lo haría. 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 como

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 cercano a la Programación Orientada a Objetos usando una C directa. A menos que esté atascado en una plataforma que no tiene un compilador de C ++, es mejor que te des por completo y solo uses un STL std :: vector .

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

Otros consejos

manténgase al tanto del tamaño de la matriz; free utiliza la cadena malloc para liberar el bloque que se asignó, que no necesariamente tiene el mismo tamaño que la matriz que solicitó

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

¿Qué pasaría 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 aritmética de punteros. Reescribamos doSomethingElse ():

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 que devolvió malloc. E incluso si get_size pasó por todos los problemas para encontrar el tamaño (es decir, de manera ineficiente), en este caso, devolvería un valor que sería incorrecto en su contexto.

Conclusión

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

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

¿Puedo recomendar una manera terrible de hacerlo?

Asigne todos sus arreglos de la siguiente manera:

void *blockOfMem = malloc(sizeof(mystruct)*n + sizeof(int));

((int *)blockofMem)[0] = n;
mystruct *structs = (mystruct *)(((int *)blockOfMem) + 1);

Entonces, siempre puedes convertir tus arrays a int * y acceder al elemento -1st.

¡Asegúrate de que libere ese puntero, y no el puntero de la matriz en sí mismo!

Además, esto probablemente causará bichos terribles que te dejarán desgarrado el cabello. Tal vez usted puede envolver las funciones de asignación en llamadas a la API o algo así.

malloc devolverá un bloque de memoria al menos tan grande como lo solicitó, pero posiblemente más grande. Así que incluso si pudiera consultar el tamaño del bloque, esto no le daría de manera confiable el tamaño de su matriz. Así que solo tendrás que modificar tu código para realizar un seguimiento del mismo.

Para una matriz de punteros puede usar una matriz terminada en NULO. La longitud puede entonces determinarse como se hace con cadenas. En su ejemplo, puede usar un atributo de estructura para marcar y luego terminar. Por supuesto, eso depende si hay un miembro que no puede ser NULL. Entonces, digamos que tiene un nombre de atributo, que debe configurarse para cada estructura en su matriz, luego puede consultar el tamaño mediante:


int size;
struct mystruct *cur;

for (cur = myarray; cur->name != NULL; cur++)
    ;

size = cur - myarray;

Por cierto, debería ser calloc (n, sizeof (struct mystruct)) en tu ejemplo.

Otros han discutido los límites de los punteros c simples y las implementaciones de stdlib.h de malloc () . Algunas implementaciones proporcionan extensiones que devuelven el tamaño de bloque asignado que puede ser más grande que el tamaño solicitado.

Si debe tener este comportamiento, puede usar o escribir un asignador de memoria especializado. Lo más sencillo sería implementar una envoltura alrededor de las funciones stdlib.h . Algo como:

void* my_malloc(size_t s);     /* Calls malloc(s), and if successful stores 
                                  (p,s) in a list of handled blocks */
void my_free(void* p);         /* Removes list entry and calls free(p) */
size_t my_block_size(void* p); /* Looks up p, and returns the stored size */
...

realmente su pregunta es: " ¿puedo averiguar el tamaño de un bloque de datos malloc'd (o calloc'd) " ;. Y como han dicho otros: no, no de manera estándar.

Sin embargo, hay implementaciones de malloc personalizadas que lo hacen, por ejemplo, http://dmalloc.com/

No estoy al tanto de una forma, pero me imagino que se trataría de rastrear las partes internas de malloc, lo que generalmente es una muy, muy mala idea.

¿Por qué no puedes almacenar el tamaño de la memoria que asignaste?

EDITAR: Si sabe que debe volver a trabajar el código para que sepa n, bueno, hágalo. Sí, podría ser rápido y fácil intentar sondear malloc, pero saber con seguridad no minimizaría la confusión y fortalecería el diseño.

Una de las razones por las que no puede preguntar a la biblioteca de malloc qué tan grande es un bloque, es que el asignador generalmente redondea el tamaño de su solicitud para cumplir con algún requisito de granularidad mínima (por ejemplo, 16 bytes). Así que si pides 5 bytes, obtendrás un bloque de tamaño 16 de vuelta. Si tomara 16 y divida por 5, obtendría tres elementos cuando en realidad solo asignó uno. Se necesitaría espacio adicional para que la biblioteca de malloc realice un seguimiento de la cantidad de bytes que solicitó en primer lugar, por lo que es mejor para usted realizar un seguimiento de eso usted mismo.

Esta es una prueba de mi tipo de rutina. Establece 7 variables para mantener los valores flotantes, luego los asigna a una matriz, que se utiliza para encontrar el valor máximo.

La magia está en la llamada a myMax:

float mmax = myMax ((float *) & amp; arr, (int) sizeof (arr) / sizeof (arr [0]));

Y eso fue mágico, ¿verdad?

myMax espera un puntero de matriz flotante (float *), así que uso & amp; arr para obtener la dirección de la matriz y la emito como un puntero flotante.

myMax también espera la cantidad de elementos en la matriz como un int. Obtengo ese valor utilizando sizeof () para darme el tamaño de bytes de la matriz y el primer elemento de la matriz, luego divido el total de bytes por el número de bytes en cada elemento. (No debemos adivinar ni codificar el tamaño de un int porque tiene 2 bytes en algún sistema y 4 en algunos como mi OS X Mac, y podría ser otra cosa en otros).

NOTA: Todo esto es importante cuando sus datos pueden tener un número variable de muestras.

Aquí está el código de prueba:

#include <stdio.h>

float a, b, c, d, e, f, g;

float myMax(float *apa,int soa){
 int i;
 float max = apa[0];
 for(i=0; i< soa; i++){
  if (apa[i]>max){max=apa[i];}
  printf("on i=%d val is %0.2f max is %0.2f, soa=%d\n",i,apa[i],max,soa);
 }
 return max;
}

int main(void)
{
 a = 2.0;
 b = 1.0;
 c = 4.0;
 d = 3.0;
 e = 7.0;
 f = 9.0;
 g = 5.0;
 float arr[] = {a,b,c,d,e,f,g};

 float mmax = myMax((float *)&arr,(int) sizeof(arr)/sizeof(arr[0]));
 printf("mmax = %0.2f\n",mmax);

 return 0;
}

En uClibc , hay una macro MALLOC_SIZE en malloc.h :

/* The size of a malloc allocation is stored in a size_t word
   MALLOC_HEADER_SIZE bytes prior to the start address of the allocation:

     +--------+---------+-------------------+
     | SIZE   |(unused) | allocation  ...   |
     +--------+---------+-------------------+
     ^ BASE             ^ ADDR
     ^ ADDR - MALLOC_HEADER_SIZE
*/

/* The amount of extra space used by the malloc header.  */
#define MALLOC_HEADER_SIZE          \
  (MALLOC_ALIGNMENT < sizeof (size_t)       \
   ? sizeof (size_t)                \
   : MALLOC_ALIGNMENT)

/* Set up the malloc header, and return the user address of a malloc block. */
#define MALLOC_SETUP(base, size)  \
  (MALLOC_SET_SIZE (base, size), (void *)((char *)base + MALLOC_HEADER_SIZE))
/* Set the size of a malloc allocation, given the base address.  */
#define MALLOC_SET_SIZE(base, size) (*(size_t *)(base) = (size))

/* Return base-address of a malloc allocation, given the user address.  */
#define MALLOC_BASE(addr)   ((void *)((char *)addr - MALLOC_HEADER_SIZE))
/* Return the size of a malloc allocation, given the user address. */
#define MALLOC_SIZE(addr)   (*(size_t *)MALLOC_BASE(addr))
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top