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