Что делает Visual Studio с удаленным указателем и почему?

Книга С++, которую я читал, гласит, что при удалении указателя с помощью оператора delete память в том месте, где он указывает, "освобождается", и ее можно перезаписать. Он также указывает, что указатель будет продолжать указывать на одно и то же место, пока оно не будет переназначено или не будет установлено на NULL.

Однако в Visual Studio 2012; это, похоже, не так!

Пример:

#include <iostream>

using namespace std;

int main()
{
    int* ptr = new int;
    cout << "ptr = " << ptr << endl;
    delete ptr;
    cout << "ptr = " << ptr << endl;

    system("pause");

    return 0;
}

Когда я компилирую и запускаю эту программу, я получаю следующий вывод:

ptr = 0050BC10
ptr = 00008123
Press any key to continue....

Очевидно, что адрес, указатель которого указывает на изменения при вызове delete!

Почему это происходит? Это как-то связано с Visual Studio?

И если delete может изменить адрес, который он указывает на anyways, почему бы не удалить, автоматически установить указатель на NULL вместо некоторого случайного адреса?

Ответ 1

Я заметил, что адрес, хранящийся в ptr, всегда был перезаписан с помощью 00008123...

Это показалось странным, поэтому я немного поработал и нашел этот пост в блоге Microsoft, содержащий раздел, посвященный теме "Автоматическая обработка указателя при удалении объектов С++".

... проверки для NULL - это общая конструкция кода, означающая, что существующая проверка для NULL в сочетании с использованием NULL в качестве значения санитарии может случайно скрыть подлинную проблему безопасности памяти, первопричиной которой действительно является необходимость в адресации.

По этой причине мы выбрали 0x8123 в качестве значения санитарии - с точки зрения операционной системы это на той же странице памяти, что и нулевой адрес (NULL), но нарушение доступа в 0x8123 лучше выделяется разработчику, поскольку более подробное внимание.

Не только объясняет, что Visual Studio делает с указателем после его удаления, но и отвечает, почему они выбрали NOT, чтобы установить его NULL автоматически!


Эта "функция" включена как часть настроек "Проверка SDL". Чтобы включить/отключить его, перейдите по адресу: ПРОЕКТ → Свойства → Свойства конфигурации → C/С++ → Общие → Проверка SDL

Чтобы подтвердить это:

Изменение этого параметра и повторный запуск одного и того же кода приводит к следующему выводу:

ptr = 007CBC10
ptr = 007CBC10

"функция" находится в кавычках, потому что в случае, когда у вас есть два указателя на одно и то же место, вызов delete будет только дезинфицировать ОДИН из них. Остальная часть будет указана на недопустимое местоположение.

Visual Studio может настроить вас на липкую ситуацию, не сумев документировать этот недостаток в своем дизайне.

Ответ 2

Вы видите побочные эффекты компиляции /sdl. Включенный по умолчанию для проектов VS2015, он обеспечивает дополнительные проверки безопасности за пределы, предоставляемые /gs. Используйте Project > Properties > C/С++ > General > SDL проверит установку, чтобы изменить его.

Цитата из статьи MSDN:

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

Имейте в виду, что установка удаленных указателей на NULL является плохой практикой при использовании MSVC. Это побеждает помощь, которую вы получаете как с помощью параметра "Отладка кучи", так и с помощью этого параметра /sdl, вы больше не можете обнаруживать недопустимые вызовы free/delete в вашей программе.

Ответ 3

В нем также указано, что указатель будет продолжать указывать на одно и то же место до тех пор, пока оно не будет переназначено или не будет установлено значение NULL.

Это определенно вводит в заблуждение информацию.

Очевидно, что адрес, указатель которого указывает на изменения при вызове delete!

Почему это происходит? Это как-то связано с Visual Studio?

Это явно соответствует спецификациям языка. ptr недействителен после вызова delete. Использование ptr после delete d является причиной для поведения undefined. Не делайте этого. Среда выполнения времени может выполнять все, что захочет, с помощью ptr после вызова delete.

И если delete может изменить адрес, который он указывает на anyways, почему бы не удалять автоматически, установите указатель на NULL вместо некоторого случайного адреса???

Изменение значения указателя на любое старое значение находится в пределах спецификации языка. Что касается изменения его в NULL, я бы сказал, что это было бы плохо. Программа будет вести себя более разумно, если значение указателя было установлено равным NULL. Однако это скроет проблему. Когда программа скомпилирована с различными настройками оптимизации или перенесена в другую среду, проблема, скорее всего, появится в самый неподходящий момент.

Ответ 4

delete ptr;
cout << "ptr = " << ptr << endl;

В целом даже чтение (например, вы делаете выше, обратите внимание: это отличается от разыменования) значения недействительных указателей (указатель становится недействительным, например, когда вы delete он) - это поведение, определенное реализацией. Это было введено в CWG # 1438. См. Также здесь.

Обратите внимание, что до того, как значения чтения недействительных указателей были undefined, то, что вы имели выше, будет undefined, что означает, что все может произойти.

Ответ 5

Я считаю, что вы запускаете какой-то режим отладки, а VS пытается переназначить ваш указатель на какое-то известное местоположение, так что дальнейшая попытка разыменовать его можно отслеживать и сообщать. Попробуйте скомпилировать/запустить ту же программу в режиме выпуска.

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

На самом деле, чем больше я думаю об этом, тем больше я нахожу, что VS виноват в этом, как обычно. Что делать, если указатель const? Это все еще изменит его?

Ответ 6

После удаления указателя память, на которую она указывает, может оставаться действительной. Чтобы продемонстрировать эту ошибку, значение указателя установлено на очевидное значение. Это действительно помогает в процессе отладки. Если значение было установлено на NULL, оно может никогда не отображаться как потенциальная ошибка в потоке программы. Таким образом, он может скрыть ошибку, когда вы позже протестируете против NULL.

Еще один момент: некоторый оптимизатор времени выполнения может проверить это значение и изменить его результаты.

В более ранние времена MS установила значение 0xcfffffff.