Правильное использование 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)?

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
}

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

Читая этот вопрос 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 находится в сфере.

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

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

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

Если realloc() возвращается NULL (и количество не 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;
}

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

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

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

Другие вопросы по тегам