How can I get the size of an array from a pointer in C?

46,008

Solution 1

No, there is no way to get this information without depending strongly on the implementation details of malloc. In particular, malloc may allocate more bytes than you request (e.g. for efficiency in a particular memory architecture). It would be much better to redesign your code so that you keep track of n explicitly. The alternative is at least as much redesign and a much more dangerous approach (given that it's non-standard, abuses the semantics of pointers, and will be a maintenance nightmare for those that come after you): store the lengthn at the malloc'd address, followed by the array. Allocation would then be:

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

n is now stored at *((unsigned long int*)p) and the start of your array is now

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

Edit: Just to play devil's advocate... I know that these "solutions" all require redesigns, but let's play it out. Of course, the solution presented above is just a hacky implementation of a (well-packed) struct. You might as well define:

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

and pass around arrInfos rather than raw pointers.

Now we're cooking. But as long as you're redesigning, why stop here? What you really want is an abstract data type (ADT). Any introductory text for an algorithms and data structures class would do it. An ADT defines the public interface of a data type but hides the implementation of that data type. Thus, publicly an ADT for an array might look like

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

In other words, an ADT is a form of data and behavior encapsulation... in other words, it's about as close as you can get to Object-Oriented Programming using straight C. Unless you're stuck on a platform that doesn't have a C++ compiler, you might as well go whole hog and just use an STL std::vector.

There, we've taken a simple question about C and ended up at C++. God help us all.

Solution 2

keep track of the array size yourself; free uses the malloc chain to free the block that was allocated, which does not necessarily have the same size as the array you requested

Solution 3

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.

Solution 4

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

Solution 5

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.

Share:
46,008
Ganymede
Author by

Ganymede

Updated on July 05, 2022

Comments

  • Ganymede
    Ganymede almost 2 years

    I've allocated an "array" of mystruct of size n like this:

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

    Later on, I only have access to p, and no longer have n. Is there a way to determine the length of the array given just the pointer p?

    I figure it must be possible, since free(p) does just that. I know malloc() keeps track of how much memory it has allocated, and that's why it knows the length; perhaps there is a way to query for this information? Something like...

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

    I know I should just rework the code so that I know n, but I'd rather not if possible. Any ideas?

  • Windows programmer
    Windows programmer over 15 years
    Actually that's a perfect reason why you should be able to ask the malloc library how big a block is. It never made sense to me that the C language was designed without such a query function.
  • Steve Jessop
    Steve Jessop over 15 years
    I once worked on a system where the standard allocate function returned both the block and its actual size (>= requested size of course). Good for things like buffers and caches, where you can profitably use any excess space.
  • Steve Jessop
    Steve Jessop over 15 years
    No good for portable code, as it doesn't work if mystruct contains any members with alignment requirement bigger than sizeof(int). Obviously not an issue on platforms where sizeof(int) is a multiple of the greatest alignment requirement of any type, but would break with eg -mfaster-structs on SPARC.
  • Steve Jessop
    Steve Jessop over 15 years
    The fact that get_size must be passed a pointer to the start of an allocated block is no bar to having it. Just don't pass in an invalid value. free() has the same constraint, and that exists...
  • dmckee --- ex-moderator kitten
    dmckee --- ex-moderator kitten over 15 years
    c-the-language is a convenient expression of assembly. The standard library is minimal as befits the tight constraints of the systems it originally ran on (and still does in embedded-land). If you want an allocator that provides lots of bells and whistles, use one.
  • dmckee --- ex-moderator kitten
    dmckee --- ex-moderator kitten over 15 years
    It's called malloc_size on OSX.
  • paercebal
    paercebal over 15 years
    Of course, but free is usually used with this in mind, along the malloc that allocated the memory. get_size would be used everywhere, including where the user is not supposed to know how the memory was allocated altogether (on the stack, via a pool, etc.).
  • ephemient
    ephemient over 15 years
    @computinglife - not necessarily, an allocator could easily keep metadata in a different part of memory than the bits it's handing out, to prevent buffer overruns from corrupting internal data structures, or put the number a few bytes earlier.
  • ephemient
    ephemient over 15 years
    In fact, glibc's default allocator places the size immediately before the returned pointer, but uses the lower bits for metadata -- thus the number must be masked off to be accurate.
  • CB Bailey
    CB Bailey over 13 years
    I think you need to read the question again. In your answer you are using the name of a statically allocated array (arr) the question is about only having a pointer to a dynamically allocated array.
  • Olie
    Olie about 11 years
    +1 for excellent explanation. My only nit is: what if it worked and had limits about what you could do with it? As dmkee points out elsewhere in comments, on OSX (my platform), it's called malloc_size(), and it works exactly as desired. There's "you can't do that" and "there's you should be very careful if you're going to do that" -- two very different things! :)
  • unwind
    unwind about 8 years
    You can't do arithmetic on a void *p like that.