Question:
Something I thought about the fact that I do not understand one kind of trivial thing.
int *p = new int[20];
delete[] p;
How does the delete[]
operator know that exactly 20 * sizeof(int)
bytes need to be freed?
What will happen as a result of executing the following code:
#include <iostream>
using namespace std;
int main()
{
int *p1 = new int[16];
int *p2 = p1;
p1 = new int[20];
cout << p1 << " --- " << p2 << " --- " << p1 - p2 << endl;
delete[] p1;
delete[] p2;
return 0;
}
and is it correct at all? Console output:
0x3d2b30 --- 0x3d2ae8 --- 18
Those. there are 64 more bits between the allocated memory areas. The last number is always even, and is at least 2 more than the size of the first array.
Another experiment (the above was on Win7/Qt/mingw), now Ubuntu14.04/Qt/g++:
int main()
{
int *p1 = new int[20];
int *p2 = p1;
p1 = new int[20];
int i;
for (i=0; i<20; i++) {
p1[i] = 0xAAAAAAAA;
p2[i] = 0xFFFFFFFF;
}
cout << "Pointers: " << p1 << " --- " << p2 << " --- " << p1 - p2 << endl;
cout << "shifted pinters: " << p1 - 1 << " --- " << p1 - 2 << endl;
cout << "values in skipped space: " << hex << *(p1-1) << " --- " << *(p1-2)
<< " --- " << *(p1-3) << " --- " << *(p1-4) << endl;
cout << "values from p2 memory: " << hex << *(p1-5) << " --- " << *(p1-6)
<< " --- " << *(p1-7) << " --- " << *(p1-8) << endl;
delete[] p1;
delete[] p2;
return 0;
}
Console output:
Pointers: 0x1ebd070 --- 0x1ebd010 --- 24
shifted pinters: 0x1ebd06c --- 0x1ebd068
values in skipped space: 0 --- 61 --- 0 --- 0
values from p2 memory: ffffffff --- ffffffff --- ffffffff --- ffffffff
Answer:
The "new with square brackets" operator stores information about the number of elements in an array. delete[]
retrieves this information and calls the destructors on the elements.
The compiler can generate code for these statements like this:
// Исходный код
struct A {
A();
~A();
};
A* a = new A[10];
delete[] a;
//---------------------------------------------------------------
// Код, который генерирует компилятор (см. примечения ниже)
// A* a = new A[10];
A* a;
{
// выделяем память
void* _mem = malloc(sizeof(int) + 10 * sizeof(A));
// ^- выделяем дополнительную память для размера массива
int* _size_ptr = (int*)_mem;
*_size_ptr = 10; // сохраняем размер
A* a = (A*)&_size_ptr[1]; // "a" указывает на память за сохраненным размером массива
for (int i = 0; i != 10; ++i)
::operator new(a + i) A; // вызываем конструкторы
}
// delete[] a;
{
// перемещаем указатель на начало выделенной памяти
int* _size_ptr = (int*)a - 1;
for (int i = *_size_ptr - 1; i >= 0; --i)
a[i].~A(); // вызываем деструкторы
// удаляем память
free(_size_ptr);
}
Note1: Actually, void* operator new[](size_t bytes)
and void operator delete[](void*)
functions are called instead of malloc
and free
. But since they call something similar to malloc/free, in the context of this question, this can be neglected.
Note2: For exception handling, the compiler will generate a try-catch block for new[]
.