Realloc на указателе NULL-значный (или undefined)
Я читал о realloc
и путался о том, что упоминалось там. Рассмотрим следующий код:
#include <stdio.h>
#include <stdlib.h>
int main () {
int* ptr = NULL;
ptr = realloc(ptr, 10*sizeof(int));
return 0;
}
Есть ли опасность в распределении памяти с помощью realloc
с использованием первоначально NULL
-значения ptr
? Если вместо:
int* ptr = NULL;
У меня было это:
int* ptr; // no value given to ptr
было бы проблемой вызвать realloc
с помощью ptr
?
Ответы
Ответ 1
Есть ли опасность в распределении памяти с помощью realloc, используя первоначально NULL-значный ptr
None
7.22.3.5
Если ptr является нулевым указателем, функция realloc ведет себя как malloc функция для заданного размера.
Для второй части:
int* ptr; // no value given to ptr
было бы проблемой вызвать realloc, используя ptr?
Если вы используете неинициализированные указатели, то это очень серьезная проблема, потому что вы не можете предсказать, какова их ценность. Функция realloc
работает корректно только для NULL
или значений, полученных из malloc
/realloc
.
В противном случае, если ptr не соответствует указателю, ранее возвращенному функция управления памятью [...] поведение не определено
Ответ 2
С указанием конкретного кода нет проблем с использованием нулевого указателя.
Если переменная ptr
неинициализирована - не установлена в 0 или NULL - тогда любой вызов realloc()
с ее использованием опасен; поведение undefined, и если вам повезет, программа выйдет из строя, но если вам повезет, она будет работать некоторое время, пока что-то не пойдет позже в программе, где будет трудно заметить, что проблема в коде, выполненном давно.
Есть те, кто утверждает, что лучше использовать malloc()
для начального распределения и realloc()
после этого. Существует некоторая справедливость в отношении предложения, не в последнюю очередь потому, что вы, вероятно, не использовали бы ptr = realloc(ptr, 0);
для освобождения памяти, даже если бы вы могли это сделать (так что вам действительно не нужны malloc()
или free()
, потому что realloc()
может выполнять все три операции). Но для стандарта C90 требуется, чтобы realloc(0, new_size)
работал эквивалентно с malloc(new_size)
, и я не знаю ни одной библиотеки C, которая ведет себя по-разному (но могут быть и некоторые из них: я использовал только несколько библиотек C, хотя, в основном, наиболее широко используемые).
Однако в более общем случае, таком как следующий код, тогда есть тонкая проблема с кодом (но это не связано с исходным нулевым указателем):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *ptr = NULL;
size_t len = 0;
char buffer[256];
while (fgets(buffer, sizeof(buffer), stdin))
{
size_t buflen = strlen(buffer) + 1;
if (buflen > len)
{
if ((ptr = realloc(ptr, buflen)) == 0) // Danger!
// ... handle memory allocation failure ...
len = buflen;
}
strcpy(ptr, buffer);
// ... do something with ptr
}
free(ptr);
return 0;
}
В чем опасность? Опасность состоит в том, что если второе или последующее распределение памяти выходит из строя, а ptr
является единственным указателем на выделенную память, вы просто перезаписываете его предыдущее значение с нулевым значением. Это означает, что вы больше не можете освобождать выделенную память с помощью ptr
- у вас просочилась память. (Для первого распределения начальное значение было 0, перезаписанное значение было равно нулю, и ничего не изменилось, утечки памяти нет. Поэтому цикл был добавлен в код.)
Правило большого пальца
- Не пишите
ptr = realloc(ptr, newsize);
Сохраните новое значение в отдельной переменной, пока не проверите его.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *ptr = NULL;
size_t len = 0;
char buffer[256];
while (fgets(buffer, sizeof(buffer), stdin))
{
size_t buflen = strlen(buffer) + 1;
if (buflen > len)
{
char *new_ptr = realloc(ptr, buflen);
if (new_ptr == 0)
// ... handle memory allocation failure ...
ptr = new_ptr;
len = buflen;
}
strcpy(ptr, buffer);
// ... do something with ptr
}
free(ptr);
return 0;
}
Этот код не утечка памяти при сбое распределения.
Вспомогательная рекомендация: не используйте переменную с именем new
; это затруднит компиляцию с помощью компилятора С++. Даже если у вас нет намерения переходить на С++ (и даже если вы, вероятно, закончите переписывать управление памятью, если вы это сделаете), нет никакой пользы в использовании ключевого слова С++ new
как имя переменной C... если только вы явно хотят предотвратить компиляцию с помощью компилятора С++.
Ответ 3
Существует ли какая-либо опасность при распределении памяти с использованием realloc с использованием первоначально NULL-значного ptr?
Нет, это будет точно как malloc
.
Если вместо:
int* ptr = NULL;
У меня было это:
int* ptr; // no value given to ptr
было бы проблемой вызвать realloc, используя ptr?
Да, будет проблема. Если realloc
не получает NULL
, он попытается развернуть память, начиная с этого места, или может попытаться выполнить free
и malloc
другую часть памяти. Поскольку неинициализированные переменные могут иметь какое-либо значение, шансы очень высоки, они не являются значениями realloc
. Если вам повезет, ваша программа немедленно потерпит крах.