Вызов free на указателе дважды

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

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

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

4 ответов


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

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

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


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

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

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

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

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

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

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

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


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

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

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

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


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

Освобождение Одного И Того Же Куска Дважды

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

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