Почему именно я не должен вызывать free () для переменных, не распределенных malloc ()? - PullRequest
9 голосов
/ 22 апреля 2010

Я где-то читал, что использование free губительно для избавления от объекта, не созданного путем вызова malloc, это правда? Зачем?

Ответы [ 7 ]

15 голосов
/ 22 апреля 2010

Это неопределенное поведение - никогда не пробуйте.

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

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

void* malloc( size_t size )
{
      void* block = tryAlloc( size + sizeof( size_t) );
      if( block == 0 ) {
          return 0;
      }
      // the following is for illustration, more service data is usually written
      *((size_t*)block) = size;
      return (size_t*)block + 1;
 }

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

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

10 голосов
/ 22 апреля 2010

По стандарту это «неопределенное поведение» - то есть «все может случиться».Однако обычно это плохо.

На практике: free указатель означает изменение кучи.Среда выполнения C практически никогда не проверяет, передается ли передаваемый указатель из кучи - это может быть дорогостоящим во времени или в памяти.Объедините эти два фактоида, и вы получите «free (non-malloced-ptr) что-то напишет куда-нибудь» - результатом могут быть некоторые «ваши» данные, измененные за вашей спиной, нарушение доступа или разрушение жизненно важных структур времени выполнения, таких какобратный адрес в стеке.

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

Вы освобождаете () указатель на локальную переменную в стеке.Вам «повезло», потому что модификация переходит в ненужное пространство стека.Тем не менее, часть стека теперь в вашем свободном списке.

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

Отладка, что.

6 голосов
/ 22 апреля 2010

Это неопределенное поведение. И логически, если поведение не определено, вы не можете быть уверены, что произошло, и если программа все еще работает должным образом.

5 голосов
/ 22 апреля 2010

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

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

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

3 голосов
/ 22 апреля 2010

Строго говоря, это не так. calloc () и realloc () также являются допустимыми объектными источниками для free (). ;)

1 голос
/ 22 апреля 2010

Посмотрите, что означает неопределенное поведение . malloc() и free() в соответствующей размещенной C реализации соответствуют стандартам. Стандарты говорят, что поведение вызова free() в блоке кучи, который не был возвращен malloc() (или чем-то обертывающим его, например, calloc()), не определено.

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

Фактически, могут быть платформы, которые (сами) определяют такое поведение. Я не знаю ни одного, но могут быть некоторые. Существует несколько реализаций сборки / записи мусора malloc (), которые могут позволить более изящно завершать работу при регистрации события. Но это реализация , а не стандарты определенное поведение.

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

0 голосов
/ 22 апреля 2010

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

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

...