Звільнення динамічно виділеної пам'яті в с програмування

(1) Термінологія. Назви операторів вводять в оману, але new - це створити об'єкт. що складається з виділення пам'яті і виклику конструктора, а delete - знищити об'єкт, тобто викликати деструктор і звільнити пам'ять. Зауважте, виділити і звільнити, а не видалити.

(2) (Злегка спрощуючи:) операція new повертає значення-покажчик, а операція delete отримує покажчик як значення. Зверніть увагу - значення в обох випадках. Це, зокрема означає, що якщо Ви зберігаєте покажчик в змінної, то значення змінної не зміниться при знищенні об'єкта. Що іноді боляче і прикро згадувати.

(3) Після звільнення пам'ять вміст пам'яті непередбачувано. Зокрема, вона може бути використана повторно.

int * a = new int # 40; # 41 ;;
* A = 5;
delete a;

int * b = new int # 40; # 41 ;;
* B = 3;
printf # 40; "a =., b =.", * a * b # 41 ;;

int * a = new int # 40; # 41 ;;
* A = 5;
delete a;
delete a;

Але є і більш цікаві варіанти:

int * a = new int # 40; # 41 ;;
int * b = a;
* A = 5;
delete a;

int * c = new int # 40; # 41 ;;
* C = 3;

int * d = new int # 40; # 41 ;;
* D = 7;
printf # 40; "c =. d =.", * c * d # 41 ;;

Тут в очі не впадає, що c був ненавмисно вилучений, і виглядає так, як ніби значення c змінилося само собою.


Для випадків важкого ожиріння (повноти повноти) картини:

(1) new і delete можна перевизначити не тільки для своїх класів, а й глобально. Цим дуже часто користуються у вбудованих системах, де стандартні alloc () / free () недоступні або небажані (уявіть собі АСУ АЕС, якої не вистачило пам'яті!).

Але - приємно - цим можна скористатися і для того, щоб трассіровать захоплення / звільнення пам'яті.

З іншого боку, вся ця чехарда все одно відбувається перед звільненням пам'яті. Тому вміст пам'яті може змінитися при free ().

(3) Усі ці «розумні» покажчики - це змінні. а не значення (smartptr ptr замість T * ptr). Зокрема, вони автоматично обнуляют збережене значення при звільненні. І або вважають посилання, або не дозволяють копіювати покажчик. Строго кажучи, до new / delete це відноситься побічно. А платити доводиться безпосередньо: і в власне розумних покажчиках, і в організації програми навколо них.

Чому після того, як блок пам'яті, на кото посилається покажчик с, позначили як вільний, цей блок змінюється?


Реалізація Вашого компілятора, ОС, архітектури ПК.
Ви враховуйте, що компілятор дуже тісно взаємодіє з ОС, коли динамічно виділяє пам'ять. Фактично, він може її просити виділити ще блок пам'яті для купи, або повертати звільнений блок в систему.

І причому при кожному запуску програми змінюється на одне і те ж число (-572 662 307).


Тому, що на Вашій системі Ви кожен раз запускаєте програму "з чистого аркуша", при однаковому оточенні, поведінці ОС, асемблерному коді Вашої програми, в спеціально виділеному ОС шматку пам'яті під вашу програму. Навіть seed з rand () має строго певне значення за замовчуванням, що гарантує постійну послідовність випадкових чисел, якщо Ви раптом їх використовуєте, і це робиться навмисне - для спрощення налагодження програм.
Але, в загальному випадку, це може бути і не так.

Ви зрозумійте одне - розбиратися що і як відбувається після видалення об'єкта, на який вказував покажчик, це означає розколупати все аж, може бути, до внутрішньої архітектури і конвеєра команд ЦП. Не варто морочитися з цим питанням, тільки якщо Ви не хакер. Програміст не повинен допускати подібних ситуацій в принципі.

непроханий гість писав (а):

Це дуже сильне твердження. Вірне далеко не завжди і далеко не скрізь. По-перше, не компілятор, а бібліотека. По-друге, бібліотека може і не взаємодіяти з віссю.


По-перше, повністю згоден, що malloc - функція бібліотечна.
По-друге, в моєму повідомленні є частка моєї апроксимації, але апроксимації розумною. Не уявляю, як бібліотека може не взаємодіяти з сучасними ОС при роботі з пам'яттю. Точніше уявляю два варіанти:
1. Заздалегідь резервується певний обмежений обсяг пам'яті під купу, і за межі цього обсягу програма вилізти не може - malloc поверне NULL при запиті пам'яті більшого обсягу, ніж доступний в даний момент в купі.
2. Відбувається спроба використати усю вільну пам'ять для даної програми. Ну це маячня за визначенням.

Таким чином, я приходжу до висновку, що функції динамічного виділення і звільнення пам'яті на сучасному етапі розвитку обчислювальної техніки, просто зобов'язані тісно взаємодіяти з ОС в цьому питанні.

непроханий гість писав (а):

Я погано знаю стандарт С ++, і тому не впевнений - чи повинен за замовчуванням new () транслюватися в виклик malloc () або не повинен.

непроханий гість писав (а):

Просто знання стандарту - це дуже специфічне знання. Воно, безсумнівно, потрібно розробнику компілятора і бібліотеки. А далі? Не знаю. Знання саме стандарту мови необхідно при написанні переносних програм - там багато різних компіляторів.


Я намагаюся писати спочатку переносимо код, навіть якщо в даний момент це не потрібно в проекті. Просто в будь-який момент може взяти та й знадобитися.
Звичайно, глибоко стандарти не вивчав, багато вже забулося, але все-таки, саме переносимість коду ставлю одним з перших вимог. Інакше, ми відмовляємося від одного з головних переваг Сі / Сі ++ - його реалізацій практично на всіх платформах. Тоді б я вже вважав за краще щось більш гарне - Яву напірімер, для спец. задач - python, php, perl і т.д. і т.п.

Але взагалі-то я мав на увазі не стандарти, а Сі ++ взагалі. Коли почитав книжку Джеффа Елджера "C ++", то зрозумів, що, як недавно сказали на БАШЕЄВ, "я знаю Сі ++, але не знаю як писати на ньому програми".
Такого накрутити можна, причому накрутити обґрунтовано, що дух захоплює. Використовується маса тонкощів, які програмістів не застосовують майже ніколи (у всякому разі, мій досвід говорить про це).

Тому я і ставлю запитання - скільки людей взагалі використовують ООП / С ++, оперуючи тенденціями, які суспільство з розвитку Сі ++ (або як там воно називається) щорічно придумує і обґрунтовує?

Не можу не сказати про Borland'цах - вони в Builder'e взагалі відключили множинне успадкування від vcl-класів (!). Також повсюдно порушується принцип инкапсулирование і т.д.

2. Відбувається спроба використати усю вільну пам'ять для даної програми. Ну це маячня по визначенню


Ну чому ж? Це абсолютно нормальна стратегія для вбудованих систем. Після або до ініціалізації ядра (в залежності від використовуваного ядра), runtime забирає всю пам'ять і управляє їй.

Бо new - це оператор, поведінка якого (неперевантаженість) має залишатися передбачуваним і ідентичним в рамках хоча б даного компілятора, що не здійснюватиметься при використанні іншої ф-ії malloc.


Чому? Чому воно стане непередбачуваним?

при цьому new краще malloc тим, що дозволяє здійснити перевірку типів


Це несуттєво. Істотно, що new викликає конструктор, а malloc () - немає. Порівняйте з placement new (файл-заголовок ).

очевидно, що new не може не взаємодіяти з malloc () - вживання їх в одній програмі цілком законно.

Такого накрутити можна, причому накрутити обґрунтовано, що дух захоплює. Використовується маса тонкощів, які програмістів не застосовують майже ніколи (у всякому разі, мій досвід говорить про це).


Програміст (будь-який) використовуючи мову (будь-який) для вирішення завдання, використовує завжди деякий підмножина мови. Так само як і мова написання статті відрізняється від пояснення у пивного ларька відрізняється від оди Володимиру Володимировичу ™.

Проблеми зазвичай виникають, коли за межами цієї підмножини залишаються занадто великі шматки мови. У цьому випадку виникають нестравний повідомлення про помилки, «незрозуміло що роблять» фрагменти програми і т.п.