Правильное использование 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 случая:
- "Когда это возможно, он просто возвращает вам тот же указатель, который вы ему передали".
- "Но если он должен перейти в какую-то другую часть памяти, чтобы найти достаточно непрерывного пространства, он вернет другой указатель (и предыдущее значение указателя станет непригодным для использования)".
- "Если
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; возвращаемый указатель может совпадать с исходным указателем.