Как освобождается память в с ++ - PullRequest
1 голос
/ 14 февраля 2020

Я понимаю, что указатель выделения памяти полностью, но освобождение памяти только на более высоком уровне. Что меня больше всего интересует, так это то, как C ++ отслеживает, какая память уже была освобождена?

int* ptr = new int;
cout << ptr;
delete ptr;
cout << ptr;
// still pointing to the same place however it knows you can't access it or delete it again
*ptr // BAD
delete ptr // BAD

Как C ++ узнает, что я освободил эту память. Если он просто превращает его в произвольные двоичные числа мусора, разве я не буду просто читать этот мусорный номер при разыменовании указателя?

Вместо этого, конечно, c ++ знает, что это как-то segfaults.

Ответы [ 4 ]

5 голосов
/ 14 февраля 2020

C ++ не отслеживает память для вас. Это не знает, это не волнует. Это зависит от вас: программист. (Де) распределение - это запрос к базовой ОС. Или, точнее, это вызов libc ++ (или, возможно, какой-то другой lib), который может или не может получить доступ к ОС, то есть деталь реализации. В любом случае ОС (или другая библиотека) отслеживает, какие части памяти вам доступны.

Когда вы пытаетесь получить доступ к памяти, которую ОС не назначала вам, ОС выдает ошибку segfault ( технически он поднимается процессором , предполагая, что он поддерживает защиту памяти, это немного сложно). И это хорошая ситуация. Таким образом, ОС говорит вам: эй, в вашем коде есть ошибка. Обратите внимание, что ОС не имеет значения, используете ли вы C ++, C, Rust или что-то еще. С точки зрения операционной системы все является машинным кодом.

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

Если он просто превращает его в произвольный двоичный мусор числа, разве я не буду просто читать это число мусора, когда разыменую указатель?

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

4 голосов
/ 14 февраля 2020

Откуда C ++ знает, что я освободил эту память.

Когда вы используете выражение delete, "C ++ знает", что вы освободили эту память.

Если он просто превращает его в произвольные двоичные числа мусора

C ++ не "превращает [освобожденную память] в произвольные двоичные числа мусора". C ++ просто делает память доступной для других распределений. Изменение состояния этой памяти может быть побочным эффектом какой-либо другой части программы, использующей эту память - что она теперь может делать свободно.

я бы просто не читал в этом числе мусора когда я разыменовываю указатель?

Когда вы косвенно через указатель, поведение программы не определено.

Вместо этого, конечно, c ++ знает, что это segfaults каким-то образом.

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

1 голос
/ 14 февраля 2020

Полагаю, вам интересно, что на самом деле делает delete. Вот оно:

  • Прежде всего, оно разрушает объект. Если у объекта есть деструктор, он вызывается и выполняет все, что для него запрограммировано.

  • delete, затем переходит к освобождению самой памяти. Это означает, что функция deallocator (::operator delete() в большинстве случаев в C ++) обычно берет объект памяти и добавляет его к своим собственным внутренним структурам данных. Т.е. он гарантирует, что при следующем вызове ::operator new() будет найден освобожденный кусок памяти. Следующая new может затем повторно использовать эту пластину памяти для других целей.

Все управление памятью происходит с использованием структур данных, которые вы не видите или должны знать, что они существуют , То, как реализация ::operator new() и ::operator delete() организует свои внутренние данные, строго и полностью зависит от реализации. Это не касается тебя.

Вас беспокоит то, что языковой стандарт определяет, что любой доступ к объекту памяти является неопределенным поведением после передачи его оператору delete. Неопределенное поведение не означает, что память должна магически исчезать sh, или что она становится недоступной, или что она заполнена мусором. Обычно ничего из этого не происходит немедленно, потому что для того, чтобы сделать память недоступной или заполнить ее мусором, потребуется явное действие со стороны ЦП, поэтому реализации обычно не затрагивают то, что записано в памяти. Вам просто запрещен дальнейший доступ, потому что теперь система может использовать память для любых других целей.

0 голосов
/ 14 февраля 2020

C ++ все еще имеет сильное наследование C, когда дело доходит до адресации памяти. И C был изобретен для создания ОС (первая версия Unix), в которой имеет смысл использовать хорошо известные адреса регистров или любые другие операции низкого уровня. Это означает, что когда вы обращаетесь к памяти через указатель, вы как программист должны знать, что там лежит, а язык просто доверяет вам.

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

Тем не менее, для обычной реализации ничего не меняется в недавно выделенном или освобожденном месте. блок, но указатели поддерживают список свободных блоков. AFAIK немногие возвращают память в ОС до конца процесса. Но свободный блок может быть позже использован повторно, поэтому, когда неосторожный программист пытается получить доступ к блоку памяти, содержащему указатели, которые были повторно использованы, SEGFAULT не далеко, потому что программа может попытаться использовать произвольную память адреса, которые не могут быть отображены для процесса.


Кстати, единственная точка , требуемая по стандарту , - это доступ к объекту после его окончания, особенно здесь с использованием указателя после того, как оператор delete вызывает неопределенное поведение. Иначе говоря, все может произойти от немедленного cra sh до нормальных результатов, проходящих через более поздний cra sh или ненормального результата в несвязанных местах программы ...

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