I read everywhere that in C++ it is not possible to get the size of a dynamic array just from the pointer pointing to that chunk of memory.
How is it possible that there is no way of getting the size of a dynamic array just from the pointer, and at the same time it is possible to free all the memory allocated by using delete []
just on the pointer, without the need of specifying the array size?
delete []
must know the size of the array, right? Therefore this information must exist somewhere. Shouldn't it?
What is wrong in my reasoning?
Dynamic arrays in C++ are declared using the new keyword. We use square brackets to specify the number of items to be stored in the dynamic array. Once done with the array, we can free up the memory using the delete operator. Use the delete operator with [] to free the memory of all array elements.
dynamically allocated arrays To dynamically allocate space, use calls to malloc passing in the total number of bytes to allocate (always use the sizeof to get the size of a specific type). A single call to malloc allocates a contiguous chunk of heap space of the passed size.
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.
It does - the allocator, or some implementation detail behind it, knows exactly what the size of the block is.
But that information is not provided to you or to the "code layer" of your program.
Could the language have been designed to do this? Sure! It's probably a case of "don't pay for what you don't use" — it's your responsibility to remember this information. After all, you know how much memory you asked for! Often times people will not want the cost of a number being passed up the call stack when, most of the time, they won't need it to be.
There are some platform-specific "extensions" that may get you what you want, like malloc_usable_size
on Linux and _msize
on Windows, though these assume that your allocator used malloc
and didn't do any other magic that may extend the size of the allocated block at the lowest level. I'd say you're still better off tracking this yourself if you really need it… or using a vector.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With