Привет, 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 я не заходил уже очень давно, и хватку немного потерял. :)
Оператор delete не меняет значения переданного указателя, но делает указатель невалидным. А вот программист несет ответственность за то, что в программе не будет разыменований невалидных указателей. Любая попытка сделать так приводит к неопределённому поведению.
Сборка персонального компьютера от Artline: умный выбор для современных пользователей