2015-12-15 3 views
10

Я преподавал на лекциях, что вызов free() на указателе дважды действительно, очень плохо. Я знаю, что это хорошая практика, установить указатель на NULL, сразу после его освобождения.Звонок бесплатный по указателю дважды

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

Я хотел бы понять, что происходит внутри, когда вы вызываете free() в том месте, которое ранее было освобождено.

+0

указатель там, где вы anotate в Direccion данных, но то, что вы на самом деле бесплатно сами данные – Netwave

+0

@DanielSanchez Да, но так как таНос подбрасывает вам, что указатель не должен освободить память/распределения построить еще обратите внимание, что он освободил место, на которое указывает указатель раньше? – Joe

+0

Если вы не используете указатель после того, как вы 'free', это бесполезно при установке его в' NULL'. И * если * вы используете указатель после того, как вы 'free', у вас есть * неопределенное поведение *, независимо от того, задаете ли вы его значение« NULL »или нет. Конечно, если вы * проверяете * на 'NULL', то это помогает, но необходимость установить указатель на' NULL' - это не то, что вы абсолютно должны делать, делайте это в индивидуальном порядке в зависимости от того, как вы используете указатель. –

ответ

7

Когда вы используете malloc, вы сообщаете ПК, что вы хотите зарезервировать место для памяти в куче только для вас. Компьютер возвращает указатель на первый байт адресного пространства.

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

Указатель по-прежнему указывает на адрес этой памяти. На этом этапе такое же пространство в куче может быть возвращено другим вызовом malloc. При вызове free во второй раз, вы не освобождая предыдущие данные, но новые данные, и это не может быть хорошо для вашей программы;)

+0

Спасибо за объяснение! – Joe

3

Чтобы ответить на ваш первый вопрос,

Так почему он не знает, был ли освобожден указатель, который он получает через free(), или нет?

потому, что спецификация malloc() в стандарте C не санкционировать это. Когда вы вызываете malloc() или семейство функций, то, что он делает, это вернуть вам указатель и внутри него хранит размер выделенной ячейки памяти в, указатель. Вот почему free() не нуждается в размере для очистки памяти.

Также, как только free() -d, что происходит с Фактически выделенная память по-прежнему зависит от реализации. Вызов free() - это всего лишь маркер , чтобы указать, что выделенная память больше не используется процессом и может быть исправлена ​​и повторно распределена при необходимости. Таким образом, отслеживание выделенного указателя в этот момент очень бесполезно. Это будет ненужным бременем для ОС, чтобы сохранить все обратную трассу.

В целях отладки, однако, некоторые реализации библиотек могут выполнять эту работу для вас, например DUMA или dmalloc, и последнее, но не менее важное, средство memcheck от Valgrind.

Теперь технически, стандарт C не определяет поведение, если вы звоните free() на указателе уже свободно ред. Это undefined behavior.

C11, глава §7.22.3.3, free() функция

[...] если аргумент не совпадает с указателем ранее возвращаемый управления памятью функции, или если пространство было высвобождены вызовом free() или realloc(), тем поведение не определено.

+1

Спасибо, это делает его довольно понятным – Joe

2

Когда вы звоните malloc, вы получаете указатель. Библиотека времени выполнения должна отслеживать память malloc. Обычно malloc не сохраняет структуры управления памятью, отделенные от памяти malloc, но в одном месте. Таким образом, malloc для x байтов фактически принимает x + n байтов, где один возможный макет состоит в том, что первые n байтов содержат связанную структуру списка с указателями на следующий (и, возможно, предыдущий) выделенный блок памяти.

Когда вы free указатель, то функция free может пройти через это внутренние структуры управления памятью и проверить, если указатель вы передаете действительный указатель, который был malloc ред. Только тогда он мог получить доступ к скрытым частям блока памяти. Но делать эту проверку было бы очень много времени, особенно если вы выделите много. Таким образом, free просто предполагает, что вы передаете действительный указатель. Это означает, что он напрямую обращается к скрытым частям блока памяти и предполагает, что указатели связанных списков там действительны.

Если вас free блок-два раза, то вы, возможно, есть проблема, что кто-то сделал новый malloc, получил память только что освобожденный, перезаписывает и второй free читает неверные указатели из него.

Установка free d указатель на NULL является хорошей практикой, потому что он помогает отлаживать. Если вы получаете доступ к 0 памяти, ваша программа может вылететь из строя, но она также может просто прочитать подозрительные значения и, возможно, потерпеть крах позже. Поиск основной причины может быть затруднен. Если вы установили free d указатели на NULL, ваша программа сразу же выйдет из строя, когда вы попытаетесь получить доступ к памяти. Это очень помогает при отладке.

0

Стандарт C только говорит, что вызов free дважды по указателю, возвращаемому malloc, и его семейная функция вызывает неопределенное поведение. Больше нет объяснения, почему это так.
Но, почему это плохо объясняется here:

освободив Same Кусок Дважды

Чтобы понять, что такая ошибка может привести, мы должны помнить, как обычно работает менеджер памяти. Часто он хранит размер выделенного фрагмента прямо перед самим блоком в памяти. Если бы мы освободили память, этот фрагмент памяти мог бы быть снова выделен другим запросом malloc(), и, таким образом, этот , свободный от, фактически освободит неправильный кусок памяти, заставив нас иметь свисающий указатель где-то еще в нашем приложении. Такие ошибки, как правило, проявляются гораздо позже, чем место в коде, где они происходили. Иногда мы их вообще не видим, но они все еще прячутся, ожидая возможности вытащить свои уродливые головы.

Другой проблема, которая может больше появляться, то, что это двойными освобождение будут сделаны после того, как освобожденный кусок был объединен вместе с соседними свободными кусками, чтобы сформировать больший свободный кусок, а затем больший кусок был перераспределен. В таком случае, когда мы попробуем free() наш кусок во второй раз, мы фактически освободим только часть фрагмента памяти, которую приложение использует в настоящее время. Это вызовет еще более неожиданные проблемы.