For example:
int *a, *b;
a = new int[10];
b = new int(12);
b = a; // I know there's memory leak, but let's ignore it first
delete [] b; // line L
What will happen? Will the entire array be deleted successfully?
What if line L is replaced by this: b = a + 1; delete [] b;
Or by this: a++; delete [] a;
And, lastly, if the length of an dynamic array is associated with the starting address, or in other words, associated with the array itself, do we have any way to get the length of it without using another variable to store the length?
Thanks a lot!
The memory block size and array length information is associated with the address of the object, which is the address of the first item of the array.
I.e. your delete[]
is safe in the given code
int *a, *b;
a = new int[10];
b = new int(12);
b = a; // I know there's memory leak, but let's ignore it first
delete [] b; // line L
However, there is no portable way to 1access the associated information. It's an implementation detail. Use a std::vector
if you need such info.
To understand why the info can't be accessed, note first that memory block size, needed for deallocation, can be larger than the array length times size of array item. So we're dealing here with two separate values. And for an array of POD item type, where item destructor calls are not necessary, there needs not be an explicitly stored array length value.
Even for an array where destructor calls are necessary, there needs not be an explicitly stored array length value associated with array. For example, in code of the pattern p = new int[n]; whatever(); delete[] p;
, the compiler can in principle choose to put the array length in some unrelated place where it can easily be accessed by the code generated for the delete
expression. E.g. it could be put in a processor register.
Depending on how smart the compiler is, there needs not necessarily be an explicitly stored memory block size either. For example, the compiler can note that in some function f
, three arrays a
, b
and c
are allocated, with their sizes known at the first allocation, and all three deallocated at the end. So the compiler can replace the three allocations with a single one, and ditto replace the three deallocations with a single one (this optimization is explicitly permitted by 2C++14 §5.3.4/10).
1 Except, in C++14 and later, in a deallocation function, which is a bit late.
2 C++14 §5.3.4/10: “An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2).
When it does so, the storage is instead provided by the implementation or provided by extending the
allocation of another new-expression. The implementation may extend the allocation of a new-expression e1
to provide storage for a new-expression e2
if …”
int *a, *b;
a = new int[10];
b = new int(12);
b = a; // I know there's memory leak, but let's ignore it first
delete [] b; // line L
Will the entire array be deleted successfully?
Yes, memory will successfully be freed since the pointer a
was set to point to an array of 10 integers
a = new int[10];
then the same memory location address is stored into b
b = a;
therefore the delete[]
line correctly deallocates the array chunk
delete [] b;
As you stated the code also leaks the integer variable 12 that was originally allocated and pointed to by b
.
As a sidenote calling delete[]
to free memory not associated with an array (specifically with a static type mismatch - see [expr.delete]/p3) would have triggered undefined behavior.
Now for some internals on where is the size information stored when allocating memory.
Compilers have some degree of freedom (cfr. §5.3.4/10) when it comes to memory allocation requests (e.g. they can "group" memory allocation requests).
When you call new
without a supplied allocator, whether it will call malloc
or not, it is implementation defined
[new.delete.single]
Executes a loop: Within the loop, the function first attempts to allocate the requested storage. Whether the attempt involves a call to the Standard C library function malloc is unspecified.
Assuming it calls malloc()
(on recent Windows systems it calls the UCRT), attempting to allocate space means doing book-keeping with paged virtual memory, and that's what malloc usually does.
Your size information gets passed along together with other data on how to allocate the memory you requested.
C libraries like glibc take care of alignment, memory guards and allocation of a new page (if necessary) so this might end up in the kernel via a system call.
There is no "standard" way to get that info back from the address only since it's an implementation detail. As many have suggested, if you were to need such an info you could
sizeof()
with an array typestd::vector<>
That info is furthermore associated with a memory allocation, both of your secondary cases (i.e. substituting the L
line with
b = a + 1; delete [] b;
or
a++; delete [] a;
will end in tears since those addresses aren't associated with a valid allocation.
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