Getting size of dynamic C-style array vs. use of delete[]. Contradiction? [duplicate]

TL;DR The operator delete[] destructs the objects and deallocates the memory. The information N (“number of elements”) is required for destructing. The information S (“size of allocated memory”) is required for deallocating. S is always stored and can be queried by compiler extensions. N is only stored if destructing objects requires calling destructors. If N is stored, where it is stored is implementation-dependent.


The operator delete [] has to do two things:

a) destructing the objects (calling destructors, if necessary) and

b) deallocating the memory.

Let’s first discuss (de)allocation, which
is delegated to the C functions malloc and free by many compilers (like GCC). The function malloc takes the number of bytes to be allocated as a parameter and returns a pointer. The function free takes only a pointer; the number of bytes is not necessary. This means that the memory allocating functions have to keep track how many bytes have been allocated. There could be a function to query how many bytes have been allocated (in Linux this can be done with malloc_usable_size, in Windows with _msize). This is not what you want because this does not tell you the size of an array but the amount of memory allocated. Since malloc is not necessarily giving you exactly as much memory as you have asked for, you cannot compute the array size from the result of malloc_usable_size:

#include <iostream>
#include <malloc.h>

int main()
{
    std::cout << malloc_usable_size(malloc(42)) << std::endl;
}

This example gives you 56, not 42: http://cpp.sh/2wdm4

Note that applying malloc_usable_size (or _msize) to the result of new is undefined behavior.

So, let’s now discuss construction and destruction of objects. Here, you have two ways of delete: delete (for single objects) and delete[] (for arrays). In very old versions of C++, you had to pass the size of the array to the delete[]-operator. As you mentioned, nowadays, this is not the case. The compiler tracks this information. GCC adds a small field prior the beginning of the array, where the size of the array is stored such that it knows how often the destructor has to be called. You might query that:

#include <iostream>

struct foo {
    char a;
    ~foo() {}
};

int main()
{
    foo * ptr = new foo[42];
    std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}

This code gives you 42: http://cpp.sh/7mbqq

Just for the protocol: This is undefined behavior, but with the current version of GCC it works.

So, you might ask yourself why there is no function to query this information. The answer is that GCC doesn’t always store this information. There might be cases where destruction of the objects is a no-operation (and the compiler is able to figure that out). Consider the following example:

#include <iostream>

struct foo {
    char a;
    //~foo() {}
};

int main()
{
    foo * ptr = new foo[42];
    std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}

Here, the answer is not 42 any more: http://cpp.sh/2rzfb

The answer is just garbage – the code was undefined behavior again.

Why? Because the compiler does not need to call a destructor, so it does not need to store the information. And, yes, in this case the compiler does not add code that keeps track how many objects have been created. Only the number of allocated bytes (which might be 56, see above) is known.

Leave a Comment