it-swarm.com.ru

Вызов бесплатный по указателю дважды

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

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

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

12
Joe

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

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

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

12
enrico.bacis

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

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

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

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

Однако для целей отладки некоторые реализации библиотек могут выполнить эту работу за вас, например, DUMA или dmalloc и, наконец, инструмент проверки правописания Valgrind, но не в последнюю очередь.

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

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

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

3
Sourav Ghosh

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

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

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

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

2
Werner Henze

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

Освобождение одного и того же куска дважды

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

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

0
haccks