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


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

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


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)

9

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

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


5

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

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

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

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


4

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


3

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

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

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

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


2

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


0

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

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


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

0

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.