How does C / C++ know how much memory to free up if it doesn't know the size of the array?
In C / C++, in order to process an array, you need to know its size. Accordingly, you should always "remember" this size and drag it into all processing functions as an argument. For example:
void foo(int* arr, size_t n) {
for (size_t i = 0; i < n; i++) {
arr[i] = i * i;
}
}
But when releasing resources, it is not necessary to know the size for some reason. You can simply call free(arr)
if the memory was allocated via malloc()
or calloc()
. Or you can use the delete[] arr;
operator if the memory was allocated via the new int[n]
operator.
Question how does C / C++ know how much do I need to free up memory if I don't know the size of the array? The free()
function and the delete[]
operator do not take the array size as arguments, but only a pointer to the array. And if C / C++ can somehow calculate the size, then why constantly "drag" it with you in a separate variable?
2 answers
These are all implementation details.
-
malloc / free
In popular implementations,
malloc
usually writes the size of the allocated block to the beginning of the allocated block. The pointer returned to you usually points to the memory immediately after that recorded size.free
knows where to look for the block size, and extracts it exactly from there. -
new / delete
By default, the regular
new
anddelete
(without[]
) simply delegate requests to allocate and deallocate raw data. memory in the samemalloc
andfree
or their analogues, viaoperator new
andoperator delete
. -
new[] / delete[]
When working with arrays of objects with trivial destructors
new[]
anddelete[]
actually behave exactly the same: call eventuallymalloc
with a correctly calculated total array size, and callfree
to free up memory.When working with arrays of objects with non-trivial destructors, everything is somewhat more complicated:
new[]
requests a little more memory frommalloc
and additionally writes the exact number of elements of the created array to the beginning of the allocated block, anddelete[]
then extracts this number and calls the correct number of destructors†.For example, if you have a 9-byte class
MyNonTrivialClass
with a non-trivial destructor, then executingMyNonTrivialClass *p = new MyNonTrivialClass[17];
Will result in the formation of a memory block with the following internal structure
+-----+-----+------+------+------ | 176 | 17 | p[0] | p[1] | ... +-----+-----+------+------+------ ^ ^ ^ | | | | | p - полученный вами указатель | | | поле типа `size_t` (8 байт), записано `new[]` | поле типа `size_t` (8 байт), записано `malloc` `new[]` запросил 161 байт = 17 * 9 + 8, размер выровнен до границы 16 байт
Specific the values may differ, but the general idea is usually the same in popular implementations.
--
† In addition to non-trivial destructors, there is another situation in the language that usually forces
new[]
to store the number of array elements at the beginning of the allocated block: when an array of objects containing an overloadedoperator delete[](void *, std::size_t)
is created, i.e., a memory release function with a second parameter of thestd::size_t
type. When freeing up memory, implementations are required to pass through this the parameter is the same value that was passed to the corresponding calloperator new[]
. To do this, they need to store the exact size of the array.Tellingly, Microsoft Visual Studio to this day (VS2019) ignores this language requirement, does not store the array size, and passes an incorrect size value to such
operator delete[]
.
Generally speaking, the memory manager knows this. For example, somewhere before the start of the allocated block, there is some service area that indicates what and how much is allocated.
Only this is the matter of the memory manager, which is related to the language "insofar as" - these are the problems of a specific implementation, what to do and how to do it. So the language is not able to calculate the dimension.