Откуда оператор delete[] узнает сколько байт следует освободить

1,00
р.
Что-то я задумался о том, что не понимаю одной вроде тривиальной вещи.
int *p = new int[20] delete[] p
Откуда оператор delete[] узнает о том, что нужно освободить именно 20 * sizeof(int) байт?
Что произойдет в результате выполнения следующего кода:
#include 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 } <br>и корректен ли он вообще? Вывод на консоль:
0x3d2b30 --- 0x3d2ae8 --- 18
Т.е. между выделенными областями памяти есть еще 64 разряда. Последнее число всегда четное, и минимум на 2 больше, чем размер первого массива.
Еще один эксперимент (выше было на Win7/Qt/mingw), теперь Ubunto14.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 } <br>Вывод на консоль:
Pointers: 0x1ebd070 --- 0x1ebd010 --- 24 shifted pinters: 0x1ebd06c --- 0x1ebd068 values in skipped space: 0 --- 61 --- 0 --- 0 values from p2 memory: ffffffff --- ffffffff --- ffffffff --- ffffffff

Ответ
Оператор new[] сохраняет информацию о количестве элементов массива. Оператор delete[] достает эту информацию и вызывает деструкторы у элементов.
Компилятор может сгенерировать код для этих операторов следующим образом:
// Исходный код 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) }
Примечание 1: На самом деле вместо malloc и free вызываются функции void* operator new[](size_t bytes) и void operator delete[](void*). Но т.к. они вызывают что-то похожее на malloc/free, то в контексте данного вопроса этим можно пренебречь.
Примечание 2: Для обработки исключений, компилятор будет генерировать try-catch блок для new[].