Примечание. Все цитаты в следующем ответе приведены в соответствии с действующим стандартом C, ISO / IEC 9899: 2018 (C18), раздел 7.22.3.4.
Во-первых, краткий обзор функции realloc()
из ISO / IEC 9899: 2018, раздел 7.22.3:
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
Несмотря на название, функция realloc()
ничего не " re выделяет". realloc()
- это , а не изменение существующего объекта в памяти. Вместо этого он выполняет своего рода процедуру «создания (нового объекта) и копирования данных».
Если size
не равно 0
и ptr
либо указывает на объект, который был выделен с помощью одной из функций управления памятью (не только malloc()
) или указывает на NULL
, realloc()
обычно создает новый объект и копирует данные из старого объекта в новый объект.
* Я говорю , обычно , потому что вы не можете предположить, что новый объект в памяти действительно выделен. Вы должны всегда проверять, был ли он выделен, проверяя, указывает ли возвращаемый указатель на NULL
.
Если размер нового объекта больше старого объекта, байты нового Объект, размер которого превышает размер старого объекта, имеет неопределенные значения. Если новый объект короче старого объекта, значения внутри разницы между ними отбрасываются. Любое другое значение остается в новом объекте так же, как и в старом.
Содержимое нового объекта должно быть таким же, как и у старого объекта до освобождения, до меньшего из новые и старые размеры. Любые байты в новом объекте, превышающие размер старого объекта, имеют неопределенные значения.
После этого , если :
ptr
- это , а не указатель на NULL
и - это указатель, ранее возвращенный функцией управления памятью, и объект, на который указывает этот указатель, не был освобожден до вызова realloc()
,
Если ptr является нулевым указателем, функция reallo c ведет себя как функция mallo c для указанного размера. В противном случае, если ptr не соответствует указателю, ранее возвращенному функцией управления памятью, или если пространство было освобождено путем вызова функции free или reallo c, поведение не определено.
size
не 0
,
Если размер равен нулю и память для нового объекта не выделена, это реализация -определено, освобожден ли старый объект. Если старый объект не освобожден, его значение должно быть неизменным.
и новый объект действительно может быть размещен, если realloc()
не вернул указатель на NULL
,
Если размер не равен нулю и память для нового объекта не выделена, старый объект не освобождается
и действительно только если все из этих посылок выполнены, realloc()
освобождает память старого объекта и возвращает указатель с адресом нового объекта в памяти.
reallo c функция освобождает старый объект, на который указывает ptr
, и возвращает указатель на новый объект, размер которого указан как size
.
Если realloc()
возвращает указатель на NULL
, новый объект не создается, и старый объект остается неизменным по его адресу в памяти.
При желании, чтобы сделать поведение "псевдо-перераспределения" почти идеальным, возможно, что новый объект после освобождение старого объект завершен (если это происходит), размещается по тому же адресу в памяти, где был сохранен старый объект.
Функция reallo c возвращает указатель на новый объект (который может имеют то же значение, что и указатель на старый объект), или нулевой указатель, если новый объект не был выделен.
В этом случае логически существует два процесса копирования данных в realloc()
, один раз в буферный объект, а затем обратно в то место, где хранился оригинальный старый объект. Буферный объект освобождается после завершения realloc()
.
Указатель ptr
, который сначала используется для указания на старый объект, не должен использоваться для возвращенного указателя. Если оператор вызова для realloc()
выглядит следующим образом:
ptr = realloc(ptr,size);
, то обычно происходит утечка памяти, если перераспределение завершается неудачно, потому что вы просто перезаписали указатель на старую память нулевым указателем. Если у вас нет другого указателя, указывающего на него, вы вытекли из памяти.
Поэтому, как правило, лучше использовать вариант для:
void *new_space = realloc(ptr, new_size);
if (new_space == NULL)
{
/* …handle out of memory condition… */
/* ptr is still valid and points to the previously allocated data */
return; /* Or otherwise do not continue to the following code */
}
ptr = new_space;
size = new_size;
Обратите внимание, что в соответствии с к тому, что я сказал выше, адрес может быть таким же, как и до вызова realloc()
.
Чтобы убедиться, что управление памятью действительно происходит таким образом, мы можем попробовать этот эксперимент:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
size_t length1 = 4;
size_t length2 = 2;
int *ptr1 = malloc(sizeof(*ptr1) * length1);
if(ptr1 == NULL)
{
printf("The object could not be allocated!\n");
return 1;
}
printf("value (not address) of ptr1 before realloc(): %p\n", (void *)ptr1);
ptr1 = realloc(ptr1,length2);
if(ptr1 == NULL)
{
printf("No new object allocated. Old object remains!\n");
return 1;
}
printf("value (not address) of ptr1 after realloc(): %p\n", (void *)ptr1);
free(ptr1);
return 0;
}
При моей попытке он вывел:
value (not address) of ptr1 before realloc(): 0x1db4010
value (not address) of ptr1 after realloc(): 0x1db4010
Таким образом, адрес, сохраненный в ptr1 после использования realloc()
, эквивалентен перед вызовом этого.
Дополнительные примечания:
realloc()
действует как malloc()
, когда ptr
является указателем NULL
:
int *ptr = NULL;
size_t length = 4;
ptr = realloc(ptr,sizeof(*ptr) * length);
должен имеют тот же эффект, что и
int *ptr;
size_t length = 4;
ptr = malloc(sizeof(*ptr) * length);
Если ptr является нулевым указателем, функция reallo c ведет себя как функция mallo c для указанного размера.
Но, по моему личному мнению, вы не должны сначала выделять динамическое c хранилище с помощью realloc()
. Я рекомендую всегда использовать malloc()
или другую функцию управления памятью. Это может вызвать некоторые трудности у будущих читателей.
- Не следует использовать
realloc(ptr,0)
в качестве замены free(ptr)
для освобождения динамической памяти c, поскольку она определяется реализацией, независимо от того, старый объект действительно освобожден или нет.
Если размер равен нулю и память для нового объекта не выделена, определяется реализацией, был ли освобожден старый объект. Если старый объект не освобожден, его значение должно быть неизменным.
Всегда использовать free()
для освобождения динамически размещенного объекта.