Очистка выделенной памяти

194
22 марта 2018, 09:08

Привет, ruSO!

Я познакомился с С++ относительно недавно, но до познания дзена мне далековато. С ноября месяца к "плюсам" не притрагивался вообще. На днях решил вернуться к заброшенному пет-проекту, который начинал писать ещё во время изучения языка, заодно вспомнить "плюсы" и продолжить своё изучение. В проекте возникла проблемка с выделением памяти под данные и её последующей очисткой. На момент написания проекта я не смог разобраться с этой проблемой, а из-за нехватки времени забросил его вовсе.

Вчера просидел в дебагере пару часиков, переписал 80% кода того класса, где возникала ошибка (архитектура класса была совершенно кривая) и избавился от ошибок выделения и очистки памяти.
Приложение, наконец, выполняется безо всяких ошибок. Напоследок решил проверить корректность очистки памяти. Снова запускаю дебаг и внимательно слежу за данными, которые удаляю. Что странно, delete myData происходит, но данные остаются. Я подумал, что за пол-года успел забыть азы C++ и то, как работают указатели. Для проверки написал по-быстрому простенький пример:

uint* a = new uint;
*a = 5;
cout<<"Before delete:\t\t"<<*a<<" on address: "<<a<<endl;
delete a;
cout<<"After delete:\t\t"<<*a<<" on address: "<<a<<endl;
*a = 7;
cout<<"After delete & set:\t"<<*a<<" on address: "<<a<<endl;

Посмотрев на результаты выполнения кода я выпал в осадок:

Before delete:      5 on address: 0x7fe994c02700
After delete:       5 on address: 0x7fe994c02700
After delete & set: 7 on address: 0x7fe994c02700
Process finished with exit code 0

По всей видимости, я не дурак, азы помню, и такая проблема даже в простых примерах, а не только в моём коде. Но и это ещё не всё! Теперь дописываем две строки после третьего cout:

delete a;
cout<<"After second delete: "<<*a<<" on address: "<<a<<endl;

Запускаем дебаггер, смотрим результат и тихо сползаем под стол:

Before delete:      5 on address: 0x7f8193500000
After delete:       5 on address: 0x7f8193500000
After delete & set: 7 on address: 0x7f8193500000
my_test_prog(1774,0x7fffafdc8340) malloc: *** error for object 0x7f8193500000: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Signal: SIGABRT (signal SIGABRT)
Terminated due to signal 6
Process finished with exit code 0

Нет, я конечно понимаю, что невозможно удалить то, что уже удалено. Но ребят, к указателю спокойно можно обращаться после первой очистки памяти (при второй попытке приложение завершается аварийно, что неудивительно), получать адрес ячейки памяти, извлекать из него значение, записывать новое и т.д. Конечно, у меня в коде нет такой глупости, как двойное удаление выделенной памяти, эту строчку я дописал лишь для проверки, мол если ячейка памяти остаётся выделенной, значит её можно удалить. Но нет. Указатель сохраняется, значение сохраняется, а удалить нельзя!

Я поставил брэкпоинты на каждую строчку и следил за указателем a. После первого вызова delete его адрес не менялся, значение не пропадало и, как видно выше, с ним спокойно можно было работать. Ну а второй delete попросту не сработал. На момент завершения программы указатель a всё ещё указывал на ячейку памяти с адресом 0x7f8193500000, даже после попытки второго delete.

Меня всё ещё посещает мысль что я нуб и чего-то не понимаю, но по логике вещей, при очистке памяти ячейка должна полностью затираться. Как так-то? Если я правильно помню, то при обращении к ячейке памяти, которая была очищена, должна вылетать то ли ошибка на этапе выполнения (с аварийным завершением программы, а у меня так только при вызове второго delete), то ли "абракадабра" вместо значения, которое было записано в эту ячейку памяти. А у меня ничего этого не происходит, поэтому и обращаюсь к вам за помощью и советом.

Заранее благодарю за помощь.
С уважением, Юрий.

Рабочее окружение:

IDE: CLion, C++17
OS:  macOS High Sierra (v10.13.3)

P.S.: если в вопросе много "воды", искренне прошу прощения. Мне всегда было сложно кратко излагать мысли "по существу", да и к тому же на ruSO я не заходил уже очень давно, и хватку немного потерял. :)

Answer 1

Оператор delete не меняет значения переданного указателя, но делает указатель невалидным. А вот программист несет ответственность за то, что в программе не будет разыменований невалидных указателей. Любая попытка сделать так приводит к неопределённому поведению.

READ ALSO
Утилита сравнения фалов

Утилита сравнения фалов

Есть утилита для сравнения файлов "testtxt" и "test1

270
Ошибка при поиске символа в массиве

Ошибка при поиске символа в массиве

Хочу в массиве найти максимально длинное словоНо ошибка строке:

279
Listeners in Java

Listeners in Java

Я не совсем понимаю что такое слушатели в Java,а точнее как их связать с кнопкой,например

248
Приведение null к 0 MongoDB

Приведение null к 0 MongoDB

В базе, в полях таблицы, может встретиться null, там где должно быть 0Как при выборе этого поля его правильно обработать? Сейчас делаю вот так

188