Pergunta

Eu alquei uma "matriz" de mystruct de tamanho n assim:

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

Mais tarde, eu só tenho acesso a p, e não tenho mais n. Existe uma maneira de determinar o comprimento da matriz dada apenas o ponteiro p?

Eu imagino isso devo ser possível, pois free(p) faz exatamente isso. Eu sei malloc() Continua com quanta memória ela alocou, e é por isso que conhece o comprimento; Talvez exista uma maneira de consultar essas informações? Algo como...

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

Eu sei que deveria apenas refazer o código para saber n, mas eu prefiro não se possível. Alguma ideia?

Foi útil?

Solução

Não, não há como obter essas informações sem depender fortemente dos detalhes da implementação de malloc. Em particular, malloc Pode alocar mais bytes do que você solicita (por exemplo, a eficiência em uma arquitetura de memória específica). Seria muito melhor redesenhar seu código para acompanhar n explicitamente. A alternativa é pelo menos Tanto quanto é um reprovação e uma abordagem muito mais perigosa (dado que não é padrão, abusa da semântica dos ponteiros e será um pesadelo de manutenção para aqueles que vêm atrás de você): Armazene o comprimenton no endereço MALLOC'D, seguido pela matriz. A alocação seria então:

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

n agora está armazenado em *((unsigned long int*)p) E o início da sua matriz é agora

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

Editar: Só para interpretar o advogado do diabo ... eu sei que essas "soluções" exigem reprojetos, mas vamos reproduzir. Obviamente, a solução apresentada acima é apenas uma implementação hacky de uma estrutura (bem compactada). Você também pode definir:

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

e passar arrInfos em vez de ponteiros crus.

Agora estamos cozinhando. Mas enquanto você estiver redesenhando, por que parar por aqui? O que você realmente quer é um tipo de dados abstrato (ADT). Qualquer texto introdutório para uma classe de algoritmos e estruturas de dados o faria. Um ADT define a interface pública de um tipo de dados, mas oculta a implementação desse tipo de dados. Assim, publicamente um ADT para uma matriz pode parecer

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

Em outras palavras, um ADT é uma forma de encapsulamento de dados e comportamento ... em outras palavras, é o mais próximo possível da programação orientada a objetos usando C. a menos que você esteja preso em uma plataforma que não Tenha um compilador C ++, você pode muito bem ir para o porco inteiro e apenas usar um STL std::vector.

Lá, tiramos uma pergunta simples sobre C e acabamos no C ++. Deus ajude a todos nós.

Outras dicas

Acompanhe o tamanho da matriz; Free usa a cadeia malloc para libertar o quadra Isso foi alocado, o que não tem necessariamente o mesmo tamanho da matriz que você solicitou

Just to confirm the previous answers: There is no way to know, just by studying a pointer, how much memory was allocated by a malloc which returned this pointer.

What if it worked?

One example of why this is not possible. Let's imagine the code with an hypothetic function called get_size(void *) which returns the memory allocated for a pointer:

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) ;
}

Why even if it worked, it would not work anyway?

But the problem of this approach is that, in C, you can play with pointer arithmetics. Let's rewrite 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 */
}

How get_size is supposed to work, as you sent the function a valid pointer, but not the one returned by malloc. And even if get_size went through all the trouble to find the size (i.e. in an inefficient way), it would return, in this case, a value that would be wrong in your context.

Conclusion

There are always ways to avoid this problem, and in C, you can always write your own allocator, but again, it is perhaps too much trouble when all you need is to remember how much memory was allocated.

Some compilers provide msize() or similar functions (_msize() etc), that let you do exactly that

May I recommend a terrible way to do it?

Allocate all your arrays as follows:

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

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

Then you can always cast your arrays to int * and access the -1st element.

Be sure to free that pointer, and not the array pointer itself!

Also, this will likely cause terrible bugs that will leave you tearing your hair out. Maybe you can wrap the alloc funcs in API calls or something.

malloc will return a block of memory at least as big as you requested, but possibly bigger. So even if you could query the block size, this would not reliably give you your array size. So you'll just have to modify your code to keep track of it yourself.

For an array of pointers you can use a NULL-terminated array. The length can then determinate like it is done with strings. In your example you can maybe use an structure attribute to mark then end. Of course that depends if there is a member that cannot be NULL. So lets say you have an attribute name, that needs to be set for every struct in your array you can then query the size by:


int size;
struct mystruct *cur;

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

size = cur - myarray;

Btw it should be calloc(n, sizeof(struct mystruct)) in your example.

Other have discussed the limits of plain c pointers and the stdlib.h implementations of malloc(). Some implementations provide extensions which return the allocated block size which may be larger than the requested size.

If you must have this behavior you can use or write a specialized memory allocator. This simplest thing to do would be implementing a wrapper around the stdlib.h functions. Some thing like:

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 */
...

really your question is - "can I find out the size of a malloc'd (or calloc'd) data block". And as others have said: no, not in a standard way.

However there are custom malloc implementations that do it - for example http://dmalloc.com/

I'm not aware of a way, but I would imagine it would deal with mucking around in malloc's internals which is generally a very, very bad idea.

Why is it that you can't store the size of memory you allocated?

EDIT: If you know that you should rework the code so you know n, well, do it. Yes it might be quick and easy to try to poll malloc but knowing n for sure would minimize confusion and strengthen the design.

One of the reasons that you can't ask the malloc library how big a block is, is that the allocator will usually round up the size of your request to meet some minimum granularity requirement (for example, 16 bytes). So if you ask for 5 bytes, you'll get a block of size 16 back. If you were to take 16 and divide by 5, you would get three elements when you really only allocated one. It would take extra space for the malloc library to keep track of how many bytes you asked for in the first place, so it's best for you to keep track of that yourself.

This is a test of my sort routine. It sets up 7 variables to hold float values, then assigns them to an array, which is used to find the max value.

The magic is in the call to myMax:

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

And that was magical, wasn't it?

myMax expects a float array pointer (float *) so I use &arr to get the address of the array, and cast it as a float pointer.

myMax also expects the number of elements in the array as an int. I get that value by using sizeof() to give me byte sizes of the array and the first element of the array, then divide the total bytes by the number of bytes in each element. (we should not guess or hard code the size of an int because it's 2 bytes on some system and 4 on some like my OS X Mac, and could be something else on others).

NOTE:All this is important when your data may have a varying number of samples.

Here's the test code:

#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;
}

In uClibc, there is a MALLOC_SIZE macro in 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top