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

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

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

ptr=(int*)malloc(sizeof(int));
ptr1=(int*)realloc(ptr,count*sizeof(int));
if(ptr1==NULL)          //reallocated pointer ptr1
{       
    printf("\nExiting!!");
    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                     
}

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

Ответ 1

Просто не назовите free() на исходном ptr по счастливому пути. По существу realloc() сделал это для вас.

ptr=(int*)malloc(sizeof(int));
ptr1=(int*)realloc(ptr,count*sizeof(int));
if(ptr1==NULL)          //reallocated pointer ptr1
{       
    printf("\nExiting!!");
    free(ptr);
    exit(0);
}
else
{
    ptr=ptr1;           // th reallocation succeeded, we can overwrite our original pointer now
}

Ответ 2

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;
}

Ответ 3

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

Чтение этот вопрос comp.lang.c, показывает 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 находится в области видимости.

Ответ 4

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

Если 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);
}

Ответ 5

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

Ответ 6

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

Глава и стих:

7.22.3.5 Функция realloc

Сводка

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 примечания; возвращаемый указатель может быть таким же, как ваш исходный указатель.