Страницы

Поиск по вопросам

Показаны сообщения с ярлыком realloc. Показать все сообщения
Показаны сообщения с ярлыком realloc. Показать все сообщения

среда, 1 января 2020 г.

Обертка над realloc

#c #инспекция_кода #memory #realloc

Написал свою обертку над realloc. Все ли здесь правильно? Может быть есть какие-то
недочеты, которых я не заметил?

#define qk_realloc(p, n) qk_realloc__(p, &p, n)

/*
=================================
[TEST] The qk_realloc__ function

Функция изменяет размер блока памяти,
на который указывает old. Если операция
выполнена успешно, то old присваивается
адрес нового блока памяти. Если операция
не выполнилась, в куче выделяется НОВЫЙ
блок памяти, в него копируется содержимое
old, старые данные освобождаются и указателю
old присваевает адрес нового блока памяти.
Если память не может быть выделена и для
нового блока, функция возвращает NULL,
при этом входные данные не изменяются.
=================================
*/

void* qk_realloc__(void* old, void* const ptrToOld, const u32 n) {
    void* new = realloc(old, n);

    if (new != NULL) {
        return *((void**) ptrToOld) = new;
    }

    // Тут должно быть сообщение типа: "не удалось выполнить realloc(n)".

    new = qk_malloc(n);

    if (new != NULL) {
        memmove(new, old, n);
        free(old);
        return *((void**) ptrToOld) = new;
    }

    // И тут похожее сообщение...

    return NULL;
}

    


Ответы

Ответ 1



Логика разработчика этой оболочки совершенно не ясна Зачем в функцию передается одновременно old и указатель на old (ptrToOld)? Если уж вы хотите передавать ptrToOld, то по нему можно получить и значение old. Зачем передавать old отдельно? Почему делается безусловное memmove(new, old, n);? А если новый размер больше старого? Также стандартный realloc разрешает передавать ему на вход нулевой указатель. Ваш memmove не учитывает эту возможность. Почему ptrToOld имеет такой странный тип - void* const? Почему этот тип не является указателем на указатель? Почему вы решили, что после неудачи realloc вы сможете сделать malloc? Теоретически, неудачи у realloc могут быть разные, но мы-то понимаем, что практически всегда неудача realloc - это просто нехватка памяти. Почему вы считаете, что если realloc не смог выделить память, то malloc сможет? *((void**) ptrToOld) - переинтрепретация памяти, возможно нарушение strict-aliasing. Почему memmove, а не memcpy? И чего именно вы пытались добиться? Первая мысль была в том, что эта оболочка призвана бороться с недостатками варианта p = realloc(p, ...), но она с ними никак не борется. Так в чем же тогда смысл?

Ответ 2



Все ли здесь правильно? Всё неправильно. Начиная с того, что нет никакой нужды делать обёртки над хорошо себя зарекомендовавшими стандартными функциями. Я даже не буду смотреть в ваш код, ответьте на простой вопрос: зачем?

пятница, 20 декабря 2019 г.

Вызовет ли realloc утечку памяти?

#c #memory_leaks #realloc

Доброго времени суток! Есть код:

// mry_char*  - это wchar_t*
// mry_charsz - это sizeof(wchar_t)

void mry_strcat(mry_char* _dest, const mry_char const* _src) {
    const u32 dl = mry_strlen(_dest);
    const u32 sl = mry_strlen(_src);

    _dest = realloc(_dest, (dl + sl + 1) * mry_charsz);
    memmove(_dest + dl, _src, sl * mry_charsz);
    _dest[dl + sl] = '\0';
}


Cppcheck пишет:

Id: memleak
Кратко: Memory leak: _dest
Сообщение: Memory leak: _dest

First included by: mary.c

Строка 69: _dest[dl + sl] = '\0';


Я так понимаю, что утечка будет, если realloc переместит блок при возрастании размера,
так ли это?

UPD

Исправил выход за границы массива.
    


Ответы

Ответ 1



В своем коде вы должны рассчитывать на то, что realloc будет всегда перемещать блок, независимо от того, увеличивается ли его размер, уменьшается ли или вообще не меняется. Эти решения принимаются на основе неподконтрольных вам обстоятельств. То, что он может иногда и не переместиться - не более чем редкая и непредсказуемая удача. Утечка у вас образуется из-за того, что новое значение указателя _dest в наружный код никак не возвращается. Память, выделенная вашим realloc в общем случае становится утечкой всегда. Либо возвращайте новый _dest из функции через возвращаемое значение, либо работайте с ним "по ссылке", т.е. делайте его mry_char** параметром. (Да, если в результате работы realloc значение указателя _dest не изменилось, то формально возвращать его "не нужно", но ожидать этого в реальном коде - это полная белиберда. В общем случае значение указателя будет меняться.) Дополнительно, в связи именно c realloc утечка также может возникнуть, если realloc не сможет выделить память вообще. Тогда он вернет null-указатель, но старую память не освободит, т.е. внутри функции доступ к памяти, изначально указываемой _dest, вы потеряете. (Будет ли это полноценной утечкой зависит от того, сохранили ли вы еще какой-то путь доступа к этой памяти. Из вашего кода этого не видно.) Также, почему памяти выделяется только под dl + sl элементов, если потом делается _dest[dl + sl] = '\0'? Явный вылет за пределы выделенной памяти.

Ответ 2



Утечки как таковой из-за realloc не появляется - если вы не забудете потом удалить выделенную память. Другое дело, что вы 1. не проверяете результат realloc (и не сохраняете его) 2. выделяете недостаточно памяти (есть выход за границу) 3. Насколько я помню, для wchar_t надо записывать не '\0', а L'\0'

пятница, 2 ноября 2018 г.

Вызовет ли realloc утечку памяти?

Доброго времени суток! Есть код:
// mry_char* - это wchar_t* // mry_charsz - это sizeof(wchar_t)
void mry_strcat(mry_char* _dest, const mry_char const* _src) { const u32 dl = mry_strlen(_dest); const u32 sl = mry_strlen(_src);
_dest = realloc(_dest, (dl + sl + 1) * mry_charsz); memmove(_dest + dl, _src, sl * mry_charsz); _dest[dl + sl] = '\0'; }
Cppcheck пишет:
Id: memleak Кратко: Memory leak: _dest Сообщение: Memory leak: _dest
First included by: mary.c
Строка 69: _dest[dl + sl] = '\0';
Я так понимаю, что утечка будет, если realloc переместит блок при возрастании размера, так ли это?
UPD
Исправил выход за границы массива.


Ответ

В своем коде вы должны рассчитывать на то, что realloc будет всегда перемещать блок, независимо от того, увеличивается ли его размер, уменьшается ли или вообще не меняется. Эти решения принимаются на основе неподконтрольных вам обстоятельств. То, что он может иногда и не переместиться - не более чем редкая и непредсказуемая удача.
Утечка у вас образуется из-за того, что новое значение указателя _dest в наружный код никак не возвращается. Память, выделенная вашим realloc в общем случае становится утечкой всегда. Либо возвращайте новый _dest из функции через возвращаемое значение, либо работайте с ним "по ссылке", т.е. делайте его mry_char** параметром.
(Да, если в результате работы realloc значение указателя _dest не изменилось, то формально возвращать его "не нужно", но ожидать этого в реальном коде - это полная белиберда. В общем случае значение указателя будет меняться.)
Дополнительно, в связи именно c realloc утечка также может возникнуть, если realloc не сможет выделить память вообще. Тогда он вернет null-указатель, но старую память не освободит, т.е. внутри функции доступ к памяти, изначально указываемой _dest, вы потеряете. (Будет ли это полноценной утечкой зависит от того, сохранили ли вы еще какой-то путь доступа к этой памяти. Из вашего кода этого не видно.)
Также, почему памяти выделяется только под dl + sl элементов, если потом делается _dest[dl + sl] = '\0'? Явный вылет за пределы выделенной памяти.