Удаление кучи и разыменование указателя на эту память - PullRequest
4 голосов
/ 30 мая 2009

Это код из упражнения:

#include <iostream>
using namespace std;

int main() {
    int n = 13;
    int* ip = new int(n + 3);
    int* ip2 = ip;
    cout << *ip << endl;
    delete ip;
    cout << *ip2 << endl;
    cout << ip << tab << ip2 << endl;
}

Когда пространство, выделенное для int в куче, удаляется, я думал, что разыменование указателя приведет к некоторой ошибке памяти. Вместо этого он возвращает 0.

Почему это?

Ответы [ 4 ]

14 голосов
/ 30 мая 2009

Разыменование недействительного указателя приводит к неопределенным результатам по спецификации. Это не гарантировано потерпеть неудачу.

Обычно (зависит от процессора / ОС / компилятора / ...) , компилятору это вообще не важно. Это просто дает то, что в данный момент находится по этому адресу памяти. Например, в архитектуре x86 вы просто видите ошибку только тогда, когда адрес находится на странице памяти, которая не сопоставлена ​​с вашим процессом (или у вашего процесса нет разрешения на доступ к нему), таким образом, CPU будет выдавать исключение (сбой защиты), с которой ОС будет обращаться должным образом (и, возможно, сбой вашего процесса). Иногда используется хитрость, чтобы доступ к адресу 0 всегда вызывал нарушение прав доступа: ОС устанавливает биты чтения / записи первой страницы адресного пространства в таблице страниц на 0, так что любой доступ к этой странице всегда будет сгенерировать исключение.

4 голосов
/ 30 мая 2009

Это поведение не определено, поэтому то, что произойдет, зависит от реализации и системы.

3 голосов
/ 30 мая 2009

Разыменование указателя ip вернет то, что происходит в этом месте памяти в это время.

Возвращает 0, потому что именно так кодируются четыре байта в ip при преобразовании в целое число.

разыменование указателя после его удаления непредсказуемо. Это может быть ноль, это может быть что-то еще, если эта память была перераспределена в другом месте.

Вам просто повезло, что это 0, когда вы запускаете свою программу.

0 голосов
/ 30 мая 2009

В дополнение к ответу Mehrdads, в gcc с glibc структуры данных, представляющие кучу памяти, сохраняются в возвращаемом блоке памяти, чтобы сохранить память (то есть, это навязчивый список). Таким образом, когда блок памяти освобождается, он добавляется в свободный список. Я предполагаю, что 0, записываемый после освобождения, указывает, что это последний элемент списка свободных блоков (первое слово размером с указатель освобожденной памяти будет содержать списки next указатель).

Если бы вам пришлось выделить и освободить больше памяти, прежде чем снова разыменовывать этот блок, значение изменилось бы, когда новый элемент добавлялся в конец списка свободных. Это один из способов, которым решения по реализации библиотеки влияют на то, что происходит во время «неопределенного» поведения. В этом случае разработчики glibc воспользовались тем фактом, что это поведение не определено, чтобы сделать их распределитель памяти более эффективным.

Если вы запустите вашу программу под valgrind, она поймает эти ошибки для вас. В любом случае, всегда держитесь подальше от неопределенного поведения, поскольку весьма вероятно, что оно будет различным на разных платформах и даже на разных сборках на одной и той же платформе (например, debug vs release).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...