You are looking at an implementation detail of how your compiler treats new[]
and delete[]
, so there isn't a definitive answer for the extra space being allocated since the answer will be specific to an implementation -- though I can provide a likely reason below.
Since this is implementation-defined, you cannot reliably detect this at runtime. More importantly, there shouldn't be any real reason to do this. Especially if you're new to C++, this fact is more of an interesting/esoteric thing to know of, but there should be no real benefit detecting this at runtime.
It's important to also be aware that this only happens with array-allocations, and not with object allocations. For example, the following will print expected numbers:
struct A {
~A(){}
};
struct B {
};
auto operator new(std::size_t n) -> void* {
std::cout << "Allocated: " << n << std::endl;
return std::malloc(n);
}
auto operator delete(void* p, std::size_t n) -> void {
std::free(p);
}
auto main() -> int {
auto* a = new A{};
delete a;
auto* b = new B{};
delete b;
}
Output:
Allocated: 1
Allocated: 1
Live Example
The extra storage only gets allocated for types with non-trivial destructors:
auto* a = new A[10];
delete[] a;
auto* b = new B[10];
delete[] b;
Outputs:
Allocated: 18
Allocated: 10
Live Example
The most likely reason why this happens is that extra bookkeeping of a single size_t
is being kept at the beginning of allocated arrays containing non-trivial destructors. This would be done so that when delete
is called, the language can know how many objects require their destructors invoked. For non-trivial destructors, its able to rely on the underlying delete
mechanics of their deallocation functions.
This hypothesis is also supported by the fact that for the GNU ABI, the extra storage is sizeof(size_t)
bytes. Building for x86_64
yields 18
for an allocation of A[10]
(8
bytes for size_t
). Building for x86
yields 14
for that same allocation (4
bytes for size_t
).
Edit
I don't recommend doing this in practice, but you can actually view this extra data from arrays. The allocated pointer from new[]
gets adjusted before being returned to the caller (which you can test by printing the address from the new[]
operator).
If you read this data into a std::size_t
, you can see that this data -- at least for the GNU ABI -- contains the exact count for the number of objects allocated.
Again, I do not recommend doing this in practice since this exploits implementation-defined behavior. But just for fun:
auto* a = new A[10];
const auto* bytes = reinterpret_cast<const std::byte*>(a);
std::size_t count;
std::memcpy(&count, bytes - sizeof(std::size_t), sizeof(std::size_t));
std::cout << "Count " << count << std::endl;
delete[] a;
The output:
Count 10
Live Example