Правильное использование realloc ()

От man realloc: функция realloc () возвращает указатель на вновь выделенную память, которая соответствующим образом выровнена для любого типа переменной и может отличаться от ptr или NULL, если запрос не выполняется.

Итак, в этом фрагменте кода:

ptr = (int *) malloc(sizeof(int));
ptr1 = (int *) realloc(ptr, count * sizeof(int));
if(ptr1 == NULL){           //reallocated pointer ptr1
    printf("Exiting!!\n");
    free(ptr);
    exit(0);
}else{
    free(ptr);          //to deallocate the previous memory block pointed by ptr so as not to leave orphaned blocks of memory when ptr=ptr1 executes and ptr moves on to another block
    ptr = ptr1;         //deallocation using free has been done assuming that ptr and ptr1 do not point to the same address                     
}

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


person user3163420    schedule 08.01.2014    source источник
comment
PS- У меня недостаточно очков репутации, чтобы задать это в другом аналогичном вопросе по SO, поэтому мне пришлось сформировать новый вопрос ..   -  person user3163420    schedule 09.01.2014


Ответы (6)


Только не вызывайте free() на своем исходном ptr на счастливом пути. По сути, realloc() сделал это за вас.

ptr = malloc(sizeof(int));
ptr1 = realloc(ptr, count * sizeof(int));
if (ptr1 == NULL) // reallocated pointer ptr1
{       
    printf("\nExiting!!");
    free(ptr);
    exit(0);
}
else
{
    ptr = ptr1;           // the reallocation succeeded, we can overwrite our original pointer now
}
person Aaron S. Kurland    schedule 08.01.2014
comment
На странице руководства realloc я не смог найти информацию, которую он сам вызовет free () ... thnx в любом случае - person user3163420; 09.01.2014
comment
@ user3163420 В самом деле? На моем Mac man realloc: ... Если недостаточно места для увеличения выделения памяти, на которое указывает ptr, realloc () создает новое распределение, копирует столько старых данных, на которые указывает ptr, сколько поместится в новое распределение , освобождает старое выделение и возвращает указатель на выделенную память. ... - person meaning-matters; 09.01.2014
comment
Я чувствую потребность во втором предложении зенита. Если бы не броски, у вас, вероятно, было бы еще два +1. - person autistic; 03.01.2016
comment
Я пытался ограничить количество изменений исходного кода, насколько это возможно. Я полностью согласен с частью без кастинга. - person Aaron S. Kurland; 19.02.2016
comment
хех ... приведение типов - это не просто бельмо на глазу, они могут вызвать проблемы в некоторых малоизвестных системах и бесполезны ... но вы сохранили их, чтобы сохранить исходный код. Теперь были введены пробелы, чтобы сделать его менее неприятным для глаз, которые также не служат функциональной цели, но ничего не сломают (в отличие от приведений типов, которые будут). Я хочу сказать, что если вы разрешите добавлять пробелы, чтобы сделать код стилистически привлекательным, не могли бы вы также разрешить удаление ненужных приведений типов, чтобы сделать код более переносимым. до стилистически привлекательного? Я не собираюсь редактировать, но вы бы вернулись, если бы я это сделал? - person autistic; 29.04.2018
comment
/ мне плачет дядя !. Сняты слепки. - person Aaron S. Kurland; 08.08.2018

Применение исправлений как правок на основе хороших комментариев ниже.

Прочитав этот вопрос на comp.lang.c, можно обнаружить 3 случая:

  1. «Когда он может, он просто возвращает вам тот же указатель, который вы ему вручили».
  2. «Но если он должен перейти в какую-то другую часть памяти, чтобы найти достаточно непрерывного пространства, он вернет другой указатель (и предыдущее значение указателя станет непригодным для использования)».
  3. «Если realloc не может найти достаточно места, он возвращает нулевой указатель и оставляет предыдущую область выделенной».

Это можно напрямую перевести в код:

int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
    // Case 3, clean up then terminate.
    free(ptr);
    exit(0);
}
else if(tmp == ptr)
{
    // Case 1: They point to the same place, so technically we can get away with
    // doing nothing.
    // Just to be safe, I'll assign NULL to tmp to avoid a dangling pointer.
    tmp = NULL;
}
else
{
    // Case 2: Now tmp is a different chunk of memory.
    ptr = tmp;
    tmp = NULL;
}

Итак, если подумать, опубликованный вами код в порядке (почти). Приведенный выше код упрощается до:

int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
    // Case 3.
    free(ptr);
    exit(0);
}
else if(ptr != tmp)
{
    ptr = tmp;
}
// Eliminate dangling pointer.
tmp = NULL;

Обратите внимание на дополнительный else if(ptr != tmp), который исключает случай 1, где вы не хотели бы вызывать free(ptr), потому что ptr и tmp относятся к одному и тому же местоположению. Кроме того, в целях безопасности я обязательно назначил NULL tmp, чтобы избежать проблем с висячими указателями, пока tmp находится в области действия.

person Keeler    schedule 08.01.2014
comment
Вы не выполнили требования, и предыдущее значение указателя станет непригодной для использования частью. В случае ptr != tmp free(ptr) неверно. - person glglgl; 19.06.2017
comment
Согласитесь с @glglgl, что этот ответ опасен, так как вы не должны звонить бесплатно (ptr) в случае 2. - person user7761803; 05.09.2017
comment
Хороший призыв, примененный как редактирование, и комментарии за вас обоих. - person Keeler; 06.09.2017
comment
@Keeler +1 для установки указателя tmp в NULL. Меня просто сильно укусило, когда я изменил указатель realloc'd внутри функции на статический. Это вызвало более поздний segfault с последующими вызовами realloc (память была освобождена между вызовами функций), так как теперь указатель сохранил свое старое (болтающееся) значение. Мне потребовалось время, чтобы понять это ... У меня есть привычка часто использовать realloc вместо malloc, но нужно быть бдительным, чтобы убедиться, что первый вызов realloc (без предварительного malloc) действительно получает указатель NULL. - person GermanNerd; 21.03.2019

OP: ... может отличаться от ptr или NULL в случае сбоя запроса.
A: Не всегда. NULL может быть возвращен законно (не ошибка), если count равно 0.

OP: Достаточно ли предположить, что перераспределенный указатель указывает на другой блок памяти, а не на тот же блок.
A: Нет

OP: Должен ли я поставить другое условие, которое будет сравнивать равенство ptr и ptr1 и исключать выполнение оператора free (ptr)?
A: Нет.

Если realloc() возвращает NULL (и count не равен 0), значение ptr все еще действует, указывая на данные без изменения размера. free(ptr) или нет, зависит от ваших целей.

Если realloc() вернет не NULL, не free(ptr), все готово освобождено.

Пример: https://codereview.stackexchange.com/questions/36662/critique-of-realloc-wrapper

#include <assert.h>
#include <stdlib.h>

int ReallocAndTest(char **Buf, size_t NewSize) {
  assert(Buf);
  void *NewBuf = realloc(*Buf, NewSize);
  if ((NewBuf == NULL) && (NewSize > 0)) {
    return 1;  // return failure
  }
  *Buf = NewBuf;
  return 0;
}
person chux - Reinstate Monica    schedule 08.01.2014

realloc вернет тот же адрес в ptr, если у него достаточно места для расширения фактического фрагмента памяти, указанного ptr. В противном случае он переместит данные в новый фрагмент и освободит старый фрагмент. Нельзя полагаться на то, что ptr1 отличается от ptr. Ваша программа ведет себя неопределенно.

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

Кстати, ни разу не забрасывайте отдачу malloc/realloc :). Ваш код должен быть таким:

ptr=malloc(sizeof(int));
ptr=realloc(ptr,count*sizeof(int));
if(ptr==NULL)
{   
    // error!    
    printf("\nExiting!!");
    // no need to free, the process is exiting :)
    exit(0);
}
person Paulo Bu    schedule 08.01.2014
comment
Здесь есть проблема: если ptr равен NULL, free (ptr) не имеет смысла. - person SirDarius; 09.01.2014
comment
Что ж, в этом есть смысл. Кроме того, программа вот-вот завершится, так что не проблема не освободить :) Исправляем :) - person Paulo Bu; 09.01.2014
comment
Это деталь реализации и в целом неверна: realloc вернет тот же адрес в ptr, если у него достаточно места для расширения фактического фрагмента памяти, на который указывает ptr. Реализации, которые разделяют выделения по размеру (например, omalloc OpenBSD), вряд ли когда-либо вернут исходный указатель, кроме случаев, когда исходный и новый размеры совпадают. - person R.. GitHub STOP HELPING ICE; 09.01.2014
comment
Для добавления примечания к приведению возврата malloc () и т. Д. +1 - person ryyker; 14.05.2015
comment
ptr=realloc(ptr,count*sizeof(int)); сломан; когда realloc возвращает NULL (который не является адресом, потому что он не указывает на объект), происходит утечка памяти, которая является старым объектом. В руководстве OpenGroup говорится: Если пространство не может быть выделено , объект останется без изменений. Стандарт C заявляет: Если память для нового объекта не может быть выделена, старый объект не освобождается и его значение не изменяется. - person autistic; 03.01.2016
comment
@autistic Не сломано. Если неспособность получить память (realloc возвращает NULL) является причиной завершения программы (как в примере Пауло Бу), утечки памяти не будет. Когда неспособность получить память является серьезной проблемой для запуска программы, такой способ использования realloc вполне подойдет. - person GermanNerd; 21.03.2019

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

person tabstop    schedule 08.01.2014
comment
Стандартный черновик C11 - n1570.pdf, который можно найти в Google. Я считаю полезным цитировать с помощью ссылок (например, щелкнув эту ссылку перенесет вас в раздел realloc файла n1570.html, который был преобразован из n1570.pdf). - person autistic; 03.01.2016

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

Глава и стих:

7.22.3.5 Функция перераспределения

Сводка

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

Описание

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

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

Возврат

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

Акцент добавлен. Обратите внимание на пункт 4; возвращаемый указатель может быть таким же, как ваш исходный указатель.

person John Bode    schedule 08.01.2014