Як видалити [] "знати" розмір масиву операндів?


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

Ви не переходите межі масиву до delete[]. Але де зберігається ця інформація? Це стандартизовано?


sourceforge.net/projects/fastmm є відкритим кодом і замінює менеджер пам'яті. Тут ви можете дізнатися, як працює управління пам’яттю та звідки надходить інформація для розподілу та видалення пам’яті.

1
Зауважте, що FastMM характерний лише для компіляторів Delphi / C ++ Builder, він не є менеджером пам'яті загального призначення для C ++. Це навіть не написано на C ++.
Ремі Лебо

Відповіді:


181

Виділяючи пам'ять на купі, ваш розподільник буде відслідковувати кількість пам'яті, яку ви виділили. Зазвичай він зберігається в сегменті "голова" безпосередньо перед пам'яттю, яку ви виділили. Таким чином, коли прийшов час звільнити пам'ять, делокатор точно знає, скільки пам'яті звільнити.


4
Зауважте, що це стосується лише розподілу масивів у C ++. Усі інші виділення залежать від розміру типу. Деякі бібліотеки зберігають усі розміри розподілу, як правило, лише в режимі налагодження.
Зан Лінкс

97
Не існує абсолютно жодної причини, чому ця інформація не повинна бути доступною програмісту. Я можу передати вказівник на функцію та звільнити її, але щоб отримати сам розмір у тій же функції, я повинен обходити додатковий параметр. Це має сенс?
Марк Рузон

26
@Mark, це має невелику кількість сенсу, оскільки теоретично він дозволяє безкоштовно розподільнику завжди зберігати розмір виділеного блоку (який може відрізнятися від розміру запитуваного блоку). Деякі конструкції алокаторів можуть потребувати цієї інформації для власних цілей, або можуть бути недостатньо складними, щоб використовувати інформацію про тип для відстеження розміру виділень без масиву купи тощо. Примушування розподільника зберігати потрібний розмір (щоб ви не Потрібно самостійно передавати розмір масиву) може бути невеликим обтяженням, однак це може вплинути на продуктивність можливих конструкцій розподільника.
Дуг МакКлін

33
Вибачте, але ця відповідь не відповідає суті. Що описано QuantumPete, це в основному "Як freeзнати, скільки пам'яті потрібно розібрати". Так, розмір блоку пам'яті зберігається "десь" malloc(як правило, у самому блоці), тож, як freeвідомо. Однак new[]/ delete[]це інша історія. Останні в основному працюють поверх malloc/ free. new[]також зберігає кількість створених ним елементів у блоці пам’яті (незалежно від malloc), щоб потім delete[]можна було отримати та використати це число для виклику потрібної кількості деструкторів.
ANT

26
Тобто фізично в блоці зберігаються два лічильника: розмір блоку (за malloc) та кількість елементів (за new[]). Зауважте, що перший не може бути використаний для обчислення останнього, оскільки загалом розмір блоку пам'яті може бути більшим, ніж дійсно необхідний для масиву запитуваного розміру. Також зауважте, що лічильник елементів масиву потрібен лише для типів з нетривіальним деструктором. Для типів з тривіальним деструктором лічильник не зберігається new[]і, звичайно, не виводиться delete[].
ANT

23

Один із підходів для компіляторів - виділити трохи більше пам’яті та зберігати кількість елементів у головному елементі.

Приклад, як це можна зробити:

Ось

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 [], коли ви виділяли пам'ять за допомогою нового [], оскільки версія масиву delete знає, що (і де) потрібно шукати, щоб звільнити потрібний об'єм пам'яті - і викликати відповідну кількість деструкторів для об'єктів.


5

В основному його розташовано в пам'яті як:

[info] [пам'ять, про яку ти просив ...]

Де інформація - це структура, використовувана вашим компілятором для зберігання кількості виділеної пам'яті, а що ні.

Від цього залежить реалізація.


4

Це не те, що є в специфікації - це залежить від реалізації.


3

Він визначений у стандарті C ++ для конкретного компілятора. Що означає магію компілятора. Він може порушуватися з нетривіальними обмеженнями вирівнювання принаймні на одній з основних платформ.

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

Майте на увазі, що operator new[]/operator delete[]! = new[]/delete[].

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


2

Оскільки масив, який слід видалити, повинен був бути створений за допомогою одного оператора "new". "Нова" операція повинна була поставити цю інформацію в купу. В іншому випадку, як додаткові використання нових знають, де закінчується купа?


0

Він не стандартизований. У час виконання Microsoft новий оператор використовує malloc (), а оператор видалення використовує free (). Отже, у цьому налаштуванні ваше запитання еквівалентне наступному: Як free () знає розмір блоку?

Існує деяка бухгалтерія, яка ведеться за лаштунками, тобто під час виконання C.


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

0

Це цікавіша проблема, ніж ви могли подумати спочатку. Ця відповідь стосується однієї можливої ​​реалізації.

По-перше, хоча на якомусь рівні ваша система повинна знати, як "звільнити" блок пам'яті, базовий malloc / free (який взагалі новий / видалити / новий [] / видалити [] зазвичай не викликає) точно не пам'ятає, скільки пам'яті ви запитаєте, він може округлюватися (наприклад, коли ви вище 4К, його часто округляють до наступного блоку розміром 4К).

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

ЗА винятком, якщо тип, що будується, не має деструктора, тоді delete [] не повинен робити нічого, крім звільнення блоку пам'яті, і тому нічого не потрібно зберігати!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.