Есть ли альтернативный способ освободить динамически выделенную память в C - не используя функцию free ()? - PullRequest
5 голосов
/ 15 апреля 2009

Я учусь на тест, и мне было интересно, если какой-либо из них эквивалентен бесплатному (ptr):

 malloc(NULL); 

 calloc(ptr); 

 realloc(NULL, ptr); 

 calloc(ptr, 0); 

 realloc(ptr, 0);

Из того, что я понимаю, ничего из этого не будет работать, потому что функция free () фактически сообщает C, что память после ptr снова доступна для использования. Извините, что это своего рода нубский вопрос, но помощь будет принята с благодарностью.

Ответы [ 3 ]

14 голосов
/ 15 апреля 2009

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

См. « Написание твердого кода » Стива Магуайра для полного анализа опасностей семейства функций malloc(). См. Веб-сайт ACCU для полного ознакомления с опасностями чтения «Написание твердого кода». Я не уверен, что это так плохо, как об этом говорят обзоры - хотя его полное отсутствие обработки const действительно датирует его (назад к началу 90-х, когда C89 был еще новым и широко не реализованным ).


D Примечания Макки о MacOS X 10.5 (BSD) интересны ...

Стандарт C99 гласит:

7.20.3.3 Функция malloc

Синопсис

#include <stdlib.h>
void *malloc(size_t size);

Описание

Функция malloc выделяет пространство для объекта, размер которого определяется размером и чье значение не определено.

Возвращает

Функция malloc возвращает либо нулевой указатель, либо указатель на выделенное пространство.

7.20.3.4 Функция realloc

Синопсис

#include <stdlib.h>
void *realloc(void *ptr, size_t size);

Описание

Функция realloc освобождает старый объект, на который указывает ptr, и возвращает указатель на новый объект, размер которого указан по размеру. Содержание нового объект должен быть таким же, как у старого объекта до освобождения, до меньшего из новых и старых размеров. Любые байты в новом объекте, превышающие размер старого объекта, имеют неопределенные значения.

Если ptr является нулевым указателем, функция realloc ведет себя как функция malloc для указанный размер. В противном случае, если ptr не совпадает с указателем, ранее возвращенным функции calloc, malloc или realloc, или если пространство было освобождено вызовом для функции free или realloc поведение не определено. Если память для нового объект не может быть размещен, старый объект не освобожден и его значение не изменилось.

Возвращает

Функция realloc возвращает указатель на новый объект (который может иметь тот же значение как указатель на старый объект), или нулевой указатель, если новый объект не может быть выделяется.


Помимо редакционных изменений из-за дополнительных заголовков и функций, стандарт ISO / IEC 9899: 2011 говорит то же самое, что и C99, но в разделе 7.22.3 вместо 7.20.3.


Страница man для realloc Solaris 10 (SPARC) гласит:

Функция realloc () изменяет размер указателя блока на ptr для размера байтов и возвращает указатель на (возможно, перемещенный) блок. Содержимое будет неизменным вплоть до меньшего из новых и старых размеров. Если новый размер блока требует перемещения блока, пространство для предыдущего создания блока освобождается. Если новый размер больше, содержимое вновь выделенной части блока не определено. Если ptr равно NULL, realloc () ведет себя как malloc () для указанного размера. Если размер равен 0, а ptr не является нулевым указателем, указанное пространство освобождается.

Это довольно явное выражение 'оно работает как free ()'.

Однако, то, что MacOS X 10.5 или BSD говорит что-то другое, подтверждает часть моего первого абзаца «Никто в здравом уме».


Есть, конечно, C99 Обоснование ... Там написано:

7.20.3 Функции управления памятью

Обработка нулевых указателей и запросов на выделение нулевой длины в определении этихотчасти руководствовался желанием поддержать эту парадигму:

OBJ * p; // pointer to a variable list of OBJs
    /* initial allocation */
p = (OBJ *) calloc(0, sizeof(OBJ));
     /* ... */
     /* reallocations until size settles */
 while(1) {
    p = (OBJ *) realloc((void *)p, c * sizeof(OBJ));
         /* change value of c or break out of loop */
 }

Этот стиль кодирования, не обязательно одобренный Комитетом, как сообщается, широко распространен использовать.

Некоторые реализации возвращали ненулевые значения для запросов на выделение нулевых байтов. Хотя эта стратегия имеет теоретическое преимущество, заключающаяся в различении «ничего» и «нуля» (нераспределенный указатель и указатель на пробел нулевой длины), она имеет более убедительный характер. Теоретический недостаток необходимости концепции объекта нулевой длины. Так как такие объекты не может быть объявлено, единственный способ, которым они могли бы появиться, было бы через такие запросы на выделение.

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

тихое изменение в C89

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

[...]

7.20.3.4 Функция realloc

Допустим нулевой первый аргумент. Если первый аргумент не равен нулю, а второй аргумент равен 0, то вызов освобождает память, указанную первым аргументом, и нулевой аргумент может быть вернулся; C99 соответствует политике запрещения объектов нулевого размера.

Новая функция C99: функция realloc была изменена, чтобы было ясно, что объект освобождается, новый объект выделяется, а содержимое нового объекта совпадает с это из старого объекта до меньшего из двух размеров. C89 попытался указать, что новый объект был тем же объектом, что и старый объект, но мог иметь другой адрес. Это конфликты с другими частями стандарта, которые предполагают, что адрес объекта является постоянным во время его продолжительность жизни. Кроме того, реализации, которые поддерживают фактическое распределение, когда размер равен нулю, не обязательно верните нулевой указатель для этого случая. C89, по-видимому, требует нулевого возвращаемого значения, и Комитет счел это слишком ограничительным.


Томас Падрон-МакКарти наблюдается :

C89 прямо говорит: «Если размер равен нулю, а ptr не является нулевым указателем, объект, на который он указывает, освобождается». Значит, они удалили это предложение в C99?

Да, они удалили это предложение, потому что оно включено в первое предложение:

Функция realloc освобождает старый объект, на который указывает ptr

Там нет комнаты для маневра; старый объект освобожден. Если запрошенный размер равен нулю, вы получаете все, что может malloc(0) вернуть, который часто (обычно) является нулевым указателем, но может быть ненулевым указателем, который также может быть возвращен в free(), но который не может быть разыменован по закону .

4 голосов
/ 15 апреля 2009
realloc(ptr, 0);

эквивалентно free(ptr); (хотя я бы не рекомендовал его использовать как таковое!)

Также: эти два вызова эквивалентны друг другу (но не бесплатны):

realloc(NULL,size)
malloc(size)
0 голосов
/ 15 апреля 2009

Последний - realloc(ptr, 0) - подходит близко. Он освободит любой выделенный блок и заменит его минимальным выделением (говорит моя страница Mac OS X 10.5). Проверьте свою локальную страницу руководства, чтобы увидеть, что она делает в вашей системе.

То есть, если ptr указывает на существенный объект, вы вернете большую часть его памяти.


Страница руководства по Debian Lenny согласна с Mitch и Jonathan ... действительно ли BSD действительно отличается от Linux в этом?


со страницы справочного руководства:

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


Справочные страницы Linux и Solaris очень чистые, а стандарт 89 года: realloc(ptr,0) работает как free(ptr). Справочная страница Mac OS выше и стандарт, приведенный Джонатаном, менее ясны, но, похоже, оставляют место для нарушения эквивалентности.

Мне было интересно почему разница: интерпретация "действовать как свободная" кажется мне очень естественной. Обе реализации, к которым у меня есть доступ, включают некоторую переменную среды, управляемую переменной, но версия BSD допускает еще много опций. Некоторые примеры:

 MallocGuardEdges             If set, add a guard page before and after
                              each large block.  
 MallocDoNotProtectPrelude    If set, do not add a guard page before large
                              blocks, even if the MallocGuardEdges envi-
                              ronment variable is set.

 MallocDoNotProtectPostlude   If set, do not add a guard page after large
                              blocks, even if the MallocGuardEdges envi-
                              ronment variable is set.

и

 MallocPreScribble            If set, fill memory that has been allocated
                              with 0xaa bytes.  This increases the likeli-
                              hood that a program making assumptions about
                              the contents of freshly allocated memory
                              will fail.
 MallocScribble               If set, fill memory that has been deallo-
                              cated with 0x55 bytes.  This increases the
                              likelihood that a program will fail due to
                              accessing memory that is no longer allo-
                              cated.

Возможно, "объект минимального размера" - это ничто (то есть эквивалент free) в обычных режимах, но что-то с некоторыми охранниками на месте. Возьми это за то, что оно того стоит.

...