Страницы

Поиск по вопросам

суббота, 30 ноября 2019 г.

Доступ к памяти из кучи после её освобождения

#cpp #память


Почему не ловлю ошибку сегментирования в этом случае?

#include 

int main(int argc, char* argv[])
{
    int* ptr = new int(47);

    std::cout << ptr << " " << *ptr << std::endl;

    /* 
      Память выделенная в heap'e должна вернуться системе.
      Или она будет доступна программе до окончания области
      видимости указателя ?
    */
    delete ptr;

    *ptr = 74;

    std::cout << ptr << " " << *ptr << std::endl;
}

    


Ответы

Ответ 1



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

Ответ 2



Как обычно работает менеджер памяти? Изначально есть большой кусок памяти, с которым можно работать, и который помечен, как свободный. Менеджер при запросе может, например, выделить кусок памяти и сохранить у себя в своем внутреннем списке пометку - вот эта память выделена, ее не трогать... (на самом деле это отдельная и очень серьезная тема, но для пояснения хватит и такого представления). Освобождается блок - менеждер делает соответствующую отметку у себя. Скорее всего, перед блоком будет находиться небольшой блок со служебной информацией - о занятости, размере занятого блока и т.п. Что происходит при освобождении с самой памятью? Ничего. Она просто помечается как свободная. Меняется только информация в служебных полях. Поэтому вы можете записать туда информацию. Или считать. Но даже если вы делаете это прямо после освобождения, без промежуточных действий - уже никто не может вам гарантировать корректности этого действия: вдруг тот же менеджер именно на это место запишет свою служебную информацию, так что вы просто ее испортите? Или другой поток успеет захватить эту память себе? Что именно произойдет, не знает никто, так что о таких ситуациях так и говорится - "неопределенное поведение". Словом, получить аварийное завершение программы в такой ситуации - это скорее везение. Невезение - не получить никакой ошибки, но запороть память, и потом, через 15 минут работы программы получить неприятности - например, совершенно неверное значение переменной, которое вы вроде бы сохраняли как совсем другое. И потом долго - очень долго искать ошибку, которая расположена совсем в другом месте программы, да еще и не воспроизводится стопроцентно при каждом запуске...

Ответ 3



Когда вы освобождаете блок памяти, а потом работаете с ним, может произойти что угодно. "Что угодно" включает: Блок памяти мог был помещён менеджером памяти в список свободных. Данные в самом блоке при этом обычно не затираются, поэтому при доступе к "удалённой" памяти она всё ещё будет содержать нужную вам информацию. Если вы используете отладочный режим или какой-нибудь защитник от ошибок, то менеджер памяти мог намеренно затереть данные в освобождённом блоке. В результате при чтении данных вы заведомо получите мусор. Освобождённый блок памяти или его часть могли быть отданы менеджером памяти какому-то другому куску вашего кода. Содержимое, соответственно, могло быть перезаписано этим другим куском кода. Данные вы прочитаете, но интерпретируете неверно, поэтому для вас это будет мусором. Менеджер памяти мог решить отдать ненужную страницу памяти вместе с вашим блоком операционной системе. При попытке доступа к "дырке" вы получите ошибку доступа, и ваш процесс рухнет. Нюанс в том, что операционная система работает с большими блоками памяти, а вашей программе хочется мелкие, поэтому менеджер памяти будет запрашивать большие куски у оси, а потом разбираться с раздачей мелких кусков в больших кусках самостоятельно. Когда вы освобождаете мелкий кусок, совсем не факт, что менеджер памяти будет освобождать большой кусок, который содержит ваш мелкий.

Комментариев нет:

Отправить комментарий