Управление памятью и перераспределение

Я просматриваю свою программу с valgrind для поиска утечек памяти. Вот один, с которым я не уверен, что делать.

==15634== 500 (224 direct, 276 indirect) bytes in 2 blocks are definitely lost in loss record 73 of 392
==15634==    at 0x4007070: realloc (vg_replace_malloc.c:429)
==15634==    by 0x807D5C2: hash_set_column(HASH*, int, char const*) (Hash.cpp:243)
==15634==    by 0x807BB15: LCD::PluginDiskstats::PluginDiskstats() (PluginDiskstats.cpp:102)
==15634==    by 0x806E021: LCD::Evaluator::Evaluator() (Evaluator.cpp:27)
==15634==    by 0x8066A87: LCD::LCDControl::LCDControl() (LCDControl.h:16)
==15634==    by 0x80667F5: main (Main.cpp:8)

Вот код:

/* add an entry to the column header table */
void hash_set_column(HASH * Hash, const int number, const char *column)
{
    if (Hash == NULL)
        return;

    Hash->nColumns++;
    Hash->Columns = (HASH_COLUMN *)realloc(Hash->Columns, Hash->nColumns * sizeof(HASH_COLUMN)); // line 243
    Hash->Columns[Hash->nColumns - 1].key = strdup(column);
    Hash->Columns[Hash->nColumns - 1].val = number;

    qsort(Hash->Columns, Hash->nColumns, sizeof(HASH_COLUMN), hash_sort_column);

}

Должен ли я что-то здесь делать в отношении управления памятью?


person Scott    schedule 23.10.2009    source источник


Ответы (6)


Проблема в том, что если realloc() завершится неудачно, функция вернет NULL, но исходный блок все равно будет выделен. Однако вы только что перезаписали указатель на этот блок и больше не можете его освободить (или использовать).

person Michael Burr    schedule 23.10.2009
comment
Ну, это отстой. Какая альтернатива перераспределению? - person Scott; 23.10.2009
comment
@Scott альтернатива такова: void * temp_ptr = realloc (foo, newsize); если (temp_ptr) foo = temp_ptr; - person asveikau; 23.10.2009
comment
@Scott - Напишите оболочку, которая сделает это за вас. Это не отстой, это очень хорошо - это означает, что вы не потеряете данные неожиданно только потому, что не смогли добавить их в набор данных. Это просто означает, что вам нужно приложить немного дополнительных усилий. - person Chris Lutz; 23.10.2009
comment
@ Крис: Понятно. Я немного неправильно понял. Думаю, теперь у меня все в порядке. :) - person Scott; 23.10.2009
comment
См. Мой другой ответ на то, на что я думаю, что valgrind действительно жалуется: stackoverflow.com/questions/1611756/ - person Michael Burr; 23.10.2009
comment
@Scott, см. stackoverflow.com/questions/1607004/ как раз для такой оболочки. - person paxdiablo; 23.10.2009
comment
Проблема не в этом - если бы это было так, программа вылетела бы на следующей строке из-за указателя NULL. Просто указатель потом ни разу не освободился. - person bdonlan; 23.10.2009

Если realloc () завершается неудачно, он возвращает ноль и исходный блок не освобождается. Эта строка:

Hash->Columns = (HASH_COLUMN *)realloc(Hash->Columns, Hash->nColumns * sizeof(HASH_COLUMN)); // line 243

не проверяет возвращаемое значение. Поэтому, если realloc () не работает, в Hash-> Columns записывается null, и исходный блок утекает.

person sharptooth    schedule 23.10.2009
comment
но если бы это была утечка, которую он видит в valgrind, было бы нулевое разыменование, и он бы рухнул. я предполагаю, что есть какой-то код, который он не предоставляет, в котором есть утечка. - person asveikau; 23.10.2009
comment
хм ... извините, чтобы исправить вышеупомянутый комментарий, подумав об этом, я полагаю, что valgrind мог теоретически отследить утечку таким образом, даже если она не была строго утечкой ... делал некоторые предположения о valgrind, что может быть неточным. - person asveikau; 23.10.2009
comment
@asveikau - Думаю, ваш первый комментарий был правильным по поводу денег. - person Michael Burr; 23.10.2009
comment
@asveikau, valgrind отслеживает только те утечки, которые на самом деле произошли. Он не будет сообщать об утечке, которая теоретически возможна, но никогда не происходила во время этого запуска программы. - person bdonlan; 23.10.2009

Valgrind не говорит, что утечка происходит в строке realloc - он говорит, что память, которая была выделена этой строкой realloc, является памятью, которая в конечном итоге утекает. Однако Valgrind не знает, где - он просто знает, что у вас больше нет ссылки на эту память, поэтому было бы невозможно free это сделать. (ОП может это знать, но очевидно, что многие ответчики этого не знают!)

Короче говоря, код, который вы вставили, не вызывает проблемы (хотя проблема, которую поднимает Майкл Берр, определенно реальна, но поскольку вы даже не проверяете NULL, возвращенный из _5 _...)

Где-то в вашем коде должен быть free(Hash->Columns), которого сейчас нет. Найдите это место - возможно, незадолго до освобождения самого Hash - и добавьте его.

person caf    schedule 23.10.2009
comment
Нет, он освобождается. Я в этом убедился: / * свободная таблица заголовков * / free (Hash- ›Columns); - person Scott; 23.10.2009
comment
Тем не менее, это то, что вам говорит Valgrind. Возможно, что-то устанавливает Hash->Columns на NULL перед этой точкой (или иным образом его перезаписывает), или, возможно, есть альтернативный путь кода, где free отсутствует. - person caf; 25.10.2009

Не уверен, что это проблема, но это потенциально проблематично. На странице руководства для realloc():

ВОЗВРАЩЕННАЯ СТОИМОСТЬ

После успешного завершения с размером, отличным от 0, realloc() возвращает указатель на (возможно, перемещенное) выделенное пространство. Если размер равен 0, возвращается либо нулевой указатель, либо уникальный указатель, который может быть успешно передан в free(). Если доступной памяти недостаточно, realloc() возвращает нулевой указатель и устанавливает для errno значение [ENOMEM].

Что произойдет, если для расширенного объекта недостаточно места, старый объект по-прежнему действителен и не будет освобожден, но realloc() вернет NULL. Таким образом, вы должны сохранить результат возврата realloc() в отдельной переменной, проверить эту переменную на NULL, а если это не так, присвоить его Hash->Columns.

person Chris Lutz    schedule 23.10.2009

Комментарий Ага - асвейкау:

но если бы это была утечка, которую он видит в valgrind, было бы нулевое разыменование, и он бы рухнул. я предполагаю, что есть какой-то код, который он не предоставляет, в котором есть утечка.

привело меня к другой проблеме - структура данных, размер которой вы изменяете, содержит указатели на строки, выделенные с помощью strdup(). если ваш realloc() вызов уменьшает выделение, вы потеряете эти указатели, не освободив их сначала должным образом. Я считаю, что Асвейкау прав, что это то, на что жалуется valgrind.

person Michael Burr    schedule 23.10.2009
comment
Но он не сокращает выделение. У него Hash->nColumns++; прямо перед этой строкой, поэтому он никак не может ее уменьшить. В этом примере он всегда становится больше. - person Chris Lutz; 23.10.2009
comment
Хороший момент - я это пропустил. Тем не менее, если realloc() возвращает NULL, почему нет ошибки (или жалобы valgrind) на последующие разыменования? Может быть, nColumns где-нибудь в мусорном ведре? (Я добираюсь сейчас) - person Michael Burr; 23.10.2009
comment
@Michael Burr - Это действительно загадка. Я подозреваю, что нам может понадобиться дополнительный код, чтобы узнать, что происходит. - person Chris Lutz; 23.10.2009
comment
Это не strdup() память, о которой Valgrind говорит, что она протекает (хотя я готов поспорить, что есть еще одно сообщение Valgrind, говорящее об этом), это память, выделенная realloc. Ошибка находится в другом месте кода - везде, где предполагается освободить эту память. - person caf; 23.10.2009

В Mac OS X и FreeBSD и др. у вас также есть функция reallocf ():

man 3 malloc | less -p reallocf 
... 
     The reallocf() function is identical to the realloc() function, except
 that it will free the passed pointer when the requested memory cannot be
 allocated.  This is a FreeBSD specific API designed to ease the problems
 with traditional coding styles for realloc causing memory leaks in
 libraries.
person hansu    schedule 23.10.2009