Как delete [] «знает» размер массива операндов?

250
Foo* set = new Foo[100];
// ...
delete [] set;

Вы не передаете границы массива delete[]. Но где хранится эта информация? Это стандартизировано?

VolkerK
источник
sourceforge.net/projects/fastmm является открытым исходным кодом и заменяет менеджер памяти. Здесь вы можете узнать, как работает управление памятью и откуда берется информация для выделения и удаления памяти.
1
Обратите внимание, что FastMM относится только к компиляторам Delphi / C ++ Builder, он не является диспетчером памяти общего назначения для C ++. Это даже не написано на C ++.
Реми Лебо

Ответы:

181

Когда вы распределяете память в куче, ваш распределитель будет отслеживать, сколько памяти вы выделили. Обычно он хранится в «головном» сегменте непосредственно перед выделенной вам памятью. Таким образом, когда пришло время освободить память, де-распределитель точно знает, сколько памяти освободить.

Питер Кюне
источник
4
Обратите внимание, что это относится только к распределению массивов в C ++. Все остальные распределения зависят от размера типа. Некоторые библиотеки хранят все размеры выделения, обычно только в режиме отладки.
Zan Lynx
97
Нет абсолютно никаких причин, почему эта информация не должна быть доступна программисту. Я могу передать указатель на функцию и освободить ее, но чтобы получить размер в той же самой функции, мне нужно передать дополнительный параметр. Это имеет какое-либо значение?
Марк Рузон
26
@ Марк, это имеет небольшой смысл, потому что теоретически он освобождает распределитель, чтобы всегда сохранять размер выделенного блока (который может отличаться от размера запрашиваемого блока). Некоторым проектам распределителя может потребоваться эта информация для их собственных целей, или они могут быть недостаточно изощренными, чтобы использовать информацию типа для отслеживания размера распределений кучи, не являющейся массивом, и т. Д. Вынуждение распределителя сохранять запрошенный размер (чтобы вы не стали необходимость передать размер массива самостоятельно) может быть небольшим препятствием, но это может повлиять на производительность при мыслимых конструкциях распределителя.
Даг МакКлин
33
Извините, но этот ответ не соответствует действительности. То, что описал QuantumPete, в основном «Как freeузнать, сколько памяти нужно освободить». Да, размер блока памяти «где-то» хранится malloc(обычно в самом блоке), так что, как freeзнать. Однако new[]/ delete[]это другая история. Последние в основном работают поверх malloc/ free. new[]также хранит количество элементов, которые он создал в блоке памяти (независимо от malloc), чтобы впоследствии delete[]можно было извлечь и использовать этот номер для вызова надлежащего количества деструкторов.
AnT
26
Т.е. физически в блоке хранятся два счетчика: размер блока (по malloc) и количество элементов (по new[]). Обратите внимание, что первое не может быть использовано для вычисления второго, так как в общем случае размер блока памяти может быть больше, чем действительно необходимо для массива запрошенного размера. Также обратите внимание, что счетчик элемента массива необходим только для типов с нетривиальным деструктором. Для типов с тривиальным деструктором счетчик не сохраняется new[]и, конечно, не восстанавливается delete[].
AnT
23

Один из подходов для компиляторов - выделить немного больше памяти и сохранить количество элементов в элементе head.

Пример того, как это можно сделать:

Вот

int* i = new int[4];

Компилятор будет выделять sizeof(int)*5байты.

int *temp = malloc(sizeof(int)*5)

Будем хранить «4» в первых sizeof(int)байтах

*temp = 4;

и установить i

i = temp + 1;

Так iбудет указывать на массив из 4 элементов, а не 5.

И удаление

delete[] i;

будет обработан следующим образом:

int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)
Avt
источник
9

Информация не стандартизирована. Однако на платформах, над которыми я работал, эта информация хранится в памяти непосредственно перед первым элементом. Поэтому вы можете теоретически получить к нему доступ и осмотреть его, однако это того не стоит.

Также именно поэтому вы должны использовать delete [], когда вы выделяете память с помощью new [], так как версия массива delete знает, что (и где) нужно искать, чтобы освободить нужное количество памяти, и вызывать соответствующее количество деструкторов. для объектов.

Daemin
источник
5

В основном это расположено в памяти как:

[info] [mem вы просили ...]

Где информация - это структура, используемая вашим компилятором для хранения объема выделенной памяти, а что нет.

Это зависит от реализации, хотя.

Франсиско Сото
источник
4

Это не то, что в спецификации - это зависит от реализации.

jeffm
источник
3

Он определен в стандарте C ++ как специфичный для компилятора. Что означает магию компилятора. Это может сломаться с нетривиальными ограничениями выравнивания по крайней мере на одной основной платформе.

Вы можете думать о возможных реализациях, понимая, что delete[]это определено только для указателей, возвращаемых new[], которые могут не совпадать с указателями, возвращаемыми operator new[]. Одной из реализаций в дикой природе является сохранение числа массивов в первом int, возвращаемом operator new[]и new[]возвращаемое смещение указателя после этого. (Вот почему нетривиальные выравнивания могут нарушаться new[].)

Имейте в виду , что operator new[]/operator delete[]! = new[]/delete[].

Плюс, это ортогонально тому, как C знает размер памяти, выделенной malloc.

MSN
источник
2

Потому что массив, который нужно «удалить», должен был быть создан с помощью единственного использования оператора «new». «Новая» операция должна была поместить эту информацию в кучу. Иначе как дополнительные пользователи new узнают, где заканчивается куча?

Джоэл Коухорн
источник
0

Это не стандартизировано. Во время выполнения Microsoft новый оператор использует malloc (), а оператор удаления - free (). Итак, в этой настройке ваш вопрос эквивалентен следующему: Как free () узнает размер блока?

За кулисами происходит некоторая бухгалтерия, то есть во время выполнения Си.

Andre
источник
5
Не правда. Перед вызовом free, delete [] должен сначала вызвать деструкторы. Для этого зная общий размер выделений недостаточен. На самом деле new [] и delete [] работают по-разному для простых и разрушенных типов в VC ++.
Сума
0

Это более интересная проблема, чем вы думаете на первый взгляд. Этот ответ об одной возможной реализации.

Во-первых, в то время как на каком-то уровне ваша система должна знать, как «освободить» блок памяти, лежащий в основе malloc / free (который обычно вызывают new / delete / new [] / delete []) не всегда точно помнит, сколько памяти Вы просите, он может округляться (например, если вы выше 4К, его часто округляют до следующего блока размером 4К).

Поэтому, даже если бы можно было получить размер блока памяти, это не говорит нам, сколько значений находится в новой [] ed памяти, поскольку оно может быть меньше. Поэтому нам нужно хранить дополнительное целое число, сообщающее нам, сколько существует значений.

ЗА ИСКЛЮЧЕНИЕМ, если конструируемый тип не имеет деструктора, то delete [] не должен делать ничего, кроме освобождения блока памяти, и, следовательно, не должен ничего хранить!

Крис Джефферсон
источник