Подтвердить что ты не робот

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?

4b9b3361

Ответ 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. Если вам повезет, ваша программа немедленно потерпит крах.