Как правильно использовать realloc(), когда он терпит неудачу и возвращает NULL?

Кто-нибудь может обобщить, как правильно использовать realloc()?

Что вы делаете, когда realloc() терпит неудачу?

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

Вот пример:

   1.  char *ptr = malloc(sizeof(*ptr) * 50);
   2.  ...
   3.  char *new_ptr = realloc(ptr, sizeof(*new_ptr) * 60);
   4.  if (!new_ptr) {
   5.      free(ptr);
   6.      return NULL;
   7.  }

Предположим, что realloc() не работает в строке 3. Правильно ли я делаю в строке 5 free()ing ptr?


person bodacydo    schedule 25.07.2010    source источник
comment
То, что у вас есть, выглядит хорошо, если вы проверили, что исходный malloc был успешным.   -  person Robert Groves    schedule 26.07.2010
comment
На самом деле, даже если исходный malloc() терпит неудачу, realloc() в порядке с нулевым указателем в качестве первого аргумента - тогда он ведет себя как malloc() и (в этом контексте) предположительно тоже потерпит неудачу (потому что, если malloc() не может выделить 50 байт, realloc(), вероятно, может' t также выделить 60).   -  person Jonathan Leffler    schedule 26.07.2010


Ответы (4)


Из http://www.c-faq.com/malloc/realloc.html

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

Поэтому вам действительно нужно освободить ранее выделенную память.

person Gian    schedule 25.07.2010

Это зависит от того, что вы хотите сделать. Когда realloc терпит неудачу, что вы хотите сделать: освободить старый блок или сохранить его в неизменном виде? Если вы хотите освободить его, то освободите его.

Имейте также в виду, что в C89/90, если вы делаете запрос realloc с нулевым целевым размером, функция realloc может возвращать нулевой указатель, даже если исходная память была успешно освобождена. Это был дефект C89/90, поскольку не было возможности отличить успех от неудачи при нулевом возврате.

В C99 этот дефект был исправлен, и была гарантирована строгая связь между нулевым возвратом и успехом/неудачей перераспределения. В C99 нулевой возврат всегда означает полный отказ realloc.

person AnT    schedule 25.07.2010
comment
Насколько я могу судить, это просто неправильно. C99 не гарантирует возврата ненулевого значения, если целевой размер равен 0; он допускает любое поведение. И кроме некоторых фанатиков GNU, большинство людей, кажется, считают поведение return 0 лучшим выбором. Более того, если целевой размер равен 0, realloc не может дать сбой, поэтому нет необходимости проверять наличие сбоя. - person R.. GitHub STOP HELPING ICE; 26.07.2010
comment
@R..: Нет, это не так. Вы просто неправильно поняли суть. Это правда, что в C99 realloc с размером 0 может возвращать null или не null, однако в C99 отношения null означают неудачу, а non-null означает, что успех был сделан строгим. т.е. если realloc возвращает null в C99, исходная память гарантированно не освобождена. В C89/90 это соотношение не было строгим: если вы вызвали realloc с размером 0 и получили нулевой результат, вы не можете сказать, была ли освобождена память или нет. - person AnT; 26.07.2010
comment
@R..: И ваше последнее замечание о realloc не может потерпеть неудачу неверно. Спецификация языка не гарантирует (и никогда не гарантировала), что realloc с нулевым размером не может дать сбой. Может. - person AnT; 26.07.2010
comment
Хорошо, я читал версию документации POSIX, в которой есть добавленное предложение: если размер равен 0, а ptr не является нулевым указателем, объект, на который указывает, освобождается. Вопреки политике POSIX не смог пометить это предложение CX. Дальнейшее чтение в других источниках предполагает, что C89 имел правильное требование, чтобы realloc с целевым размером 0 действовал как свободный, и C99 нарушил его. Любая реальная стоящая реализация в любом случае будет следовать POSIX, по крайней мере... - person R.. GitHub STOP HELPING ICE; 26.07.2010
comment
Я не могу найти такого требования, которое, как вы утверждаете, существует. Можете ли вы предоставить цитату? - person R.. GitHub STOP HELPING ICE; 26.07.2010
comment
@R..: realloc не может действовать как free, потому что realloc должен что-то возвращать, и даже C89/90 допускал ненулевые возвраты. Вот почему на самом деле realloc с размером 0 всегда действовало как free, за которым следует malloc(0). C89/90 просто не смог должным образом описать это поведение и, вероятно, по этой причине не смог обеспечить надежный метод обнаружения отказов. - person AnT; 26.07.2010
comment
@R ..: Я не могу найти такого требования, о котором вы утверждаете, - о чем конкретно вы говорите? - person AnT; 26.07.2010
comment
POSIX требует, чтобы realloc с целевым размером 0 действовал как free, так что вполне возможно. См. opengroup.org/onlinepubs/9699919799/functions/realloc.html. - person R.. GitHub STOP HELPING ICE; 26.07.2010
comment
Предполагаемое требование, что realloc может возвращать 0 только в том случае, если исходный объект не освобожден. - person R.. GitHub STOP HELPING ICE; 26.07.2010
comment
@R..: Требуется? Вы получили это задом наперёд. POSIX не может ничего требовать от языка C. В то же время POSIX может требовать что угодно для себя, если это соответствует ограничениям, определенным спецификацией языка C. Выполнение роли free соответствует этим ограничениям, так что это совершенно нормально. Итак, что вы пытаетесь сказать? Что он действует как free в POSIX? Большой. Но тема C вообще, а не POSIX. - person AnT; 26.07.2010
comment
@R..: Требование возврата null означает, что сбой явно указан в C99. Кроме того, вы можете ознакомиться с Обоснованием C99 (open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf), где объясняется, почему спецификация realloc была так изменена: чтобы разрешить реализациям, которые хотят возвращать не- пустые запросы на выделение нулевого размера. Судя по всему, такие реализации существуют. - person AnT; 26.07.2010
comment
@R..: цитата из вашей ссылки Любой конфликт между описанными здесь требованиями и стандартом ISO C является непреднамеренным. - person u0b34a0f6ae; 26.07.2010
comment
Любая реализация realloc в системе POSIX должна соответствовать требованиям POSIX. Конечно, это не означает, что другие реализации C должны делать то же самое, но если POSIX не имеет серьезного дефекта, то очень ясно, что realloc действует как free, когда целевой размер равен 0, является одним из возможных (не говоря уже о наиболее вероятном ) поведение реализации. - person R.. GitHub STOP HELPING ICE; 26.07.2010
comment
POSIX также явно требует, чтобы fwrite была бинарной функцией ввода/вывода. Может ли POSIX требовать этого? Да, для POSIX. Тем не менее, fwrite обычно не является бинарной функцией ввода-вывода в языке C. - person AnT; 26.07.2010
comment
@R..: Делая это, POSIX фактически запрещает реализации, которые хотят зарезервировать ненулевые указатели для запросов на выделение нулевого размера. Может ли POSIX сделать это? Да, оно может. Оскорбительные реализации просто не будут POSIX. Однако язык C не хочет запрещать такие реализации. Отсюда и более сложная спецификация realloc. - person AnT; 26.07.2010
comment
Я потерял смысл того, что вы пытаетесь донести. 7.20.3.4 в Обосновании ясно сказано, что целевой размер 0 освобождает объект и что в этом случае realloc может вернуть 0. Ваше утверждение о том, что возвращаемое значение 0 подразумевает, что исходный объект не был освобожден, является просто ложно. - person R.. GitHub STOP HELPING ICE; 26.07.2010
comment
Я пытаюсь подчеркнуть, что код OP идеально подходит для C99, но теоретически может быть недействительным для C89/90, даже если для этого потребуется злонамеренно нелогичная реализация C89/90. - person AnT; 26.07.2010
comment
@R..: Я понимаю, что вы имеете в виду в Обосновании, но, похоже, это либо дефект, либо плохая формулировка в Обосновании. Цель C99 очень явная и ясная: когда realloc возвращает нулевой указатель, это всегда указывает на сбой, а это означает, что старая память не освобождается. Это уже объяснялось и разъяснялось Комитетом много раз. - person AnT; 26.07.2010
comment
Вы просто ошибаетесь. C99 не выдвигает таких требований, и вы не предоставили никаких доказательств того, что они выполняются. Я предоставил достаточно доказательств того, что реализации могут и делают возвращать 0 в случае безотказной работы. - person R.. GitHub STOP HELPING ICE; 26.07.2010
comment
@R..: Нет, я абсолютно прав, и формулировка в C99 очень ясна и ясна. Текст C99 — это все, что нужно. Вы не предоставили никаких доказательств, кроме ссылки на Обоснование (которое я, по иронии судьбы, упомянул первым). В любом случае, независимо от количества предоставленных доказательств, пока ему не удастся свергнуть С99, эти доказательства не имеют веса. Единственное, что вы предоставили в достаточном количестве, это ссылки на POSIX, что до смешного неуместно в данном случае. - person AnT; 26.07.2010
comment
Покажите цитату, пожалуйста, любезный. До сих пор все, что вы сделали, это продемонстрировали близость к ненулевому поведению, которое glibc так любит, а все остальные, кажется, ненавидят. - person R.. GitHub STOP HELPING ICE; 26.07.2010
comment
В S 7.20.3 (стр. 313) стандарт C99 говорит о функциях управления памятью. В нем говорится: если размер запрошенного пространства равен нулю, поведение определяется реализацией: либо возвращается нулевой указатель, либо поведение такое, как если бы размер был некоторым ненулевым значением, за исключением того, что возвращенный указатель не должен использоваться для доступа объект. Это из черновика TC3 (у меня нет окончательной версии, но я не думаю, что будет разница). - person Dean Harding; 27.07.2010
comment
Должен ли мод удалять раздел комментариев? Убери это здесь на ступеньку ниже, я пытаюсь спать FFS. - person ; 27.07.2010
comment
@Dean Harding: Нет никаких споров о законности нулевого возврата. Вопрос о постусловиях в ситуации, когда возвращается нулевой указатель. В этом случае старая память освобождается или нет? - person AnT; 27.07.2010
comment
@AudreyT: А, понятно, я не могу ответить на этот вопрос :) - person Dean Harding; 28.07.2010

Если realloc выйдет из строя, я не думаю, что вы захотите удалить исходный блок, так как вы его потеряете. Похоже, что realloc изменит размер старого блока (или вернет указатель на новое место) и в случае успеха вернет указатель на старый блок (или новое место), а в случае неудачи вернет NULL. Если он не может выделить новый блок, старый блок остается нетронутым.

person Cervo    schedule 25.07.2010

Редактировать: Исправление, некоторые люди ругают меня за то, что я сказал, то, как вы разместили свой указатель, кажется, является лучшей практикой среди них, меня учили всегда использовать приведения типов в sizeof(), но, видимо, ваш путь более правильный, поэтому не обращайте внимания на то, что я сказал =)

Взгляните на http://en.wikipedia.org/wiki/Malloc#realloc раньше, возможно, принесло вам пользу.

Вы не совсем понимаете sizeof() - оно имеет значение размера аргумента, который вы ему передаете в байтах. Например, sizeof(int) будет равно 4 в большинстве 32-битных систем, но вы все равно должны использовать sizeof(int) вместо 4, потому что компиляция вашего кода в 64-битной системе (просто в качестве примера) сделает это значение равным 8, и ваш код все равно будет нормально компилироваться. . Для чего вы выделяете память? Указатели? Если это так, вы должны использовать вместо этого sizeof(void*) (вы можете сказать sizeof(int*), но общепринято не указывать компилятору, что вы хотите сохранить в этих указателях, поскольку все указатели должны быть одинакового размера, поэтому большинство программистов говорят sizeof(void*)), если вам нужно пробел для символов используйте sizeof(char) и так далее.

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

person Vasiliy Sharapov    schedule 25.07.2010
comment
Его использование sizeof прекрасно и правильно. sizeof(*ptr) — это размер объекта, на который указывает ptr, поэтому ptr = malloc(sizeof(*ptr) * N); является правильным и идиоматичным (хотя лично я бы написал его как ptr = malloc(N * sizeof ptr[0]);, но это просто стиль) - person caf; 26.07.2010
comment
Как раз наоборот: то, как ОП делает в своем коде, именно так и должно быть. Запросы на выделение памяти должны быть сделаны как можно более независимыми от типа: без приведения к результату функции выделения памяти и без имен типов под sizeof. Странная привычка использовать имена типов под sizeof в malloc запросах (и подобных) давно заслужила свое место в мусорной корзине программирования на C. Имена типов принадлежат объявлениям. Если это не объявление, имена типов не допускаются (насколько это возможно). - person AnT; 26.07.2010
comment
foo = malloc(count * sizeof *foo); — это стандартная идиома, чтобы убедиться, что вы выбрали правильный размер. Вы обращаетесь с автором вопроса так, как будто он ничего не знает о C, но при этом упускает что-то довольно фундаментальное. - person R.. GitHub STOP HELPING ICE; 26.07.2010