вызов calloc — утечка памяти valgrind

Следующий код является примером из библиотеки меню NCURSES. Я не уверен, что может быть не так с кодом, но valgrind сообщает о некоторых проблемах. Любые идеи...

==4803== 1,049 (72 direct, 977 indirect) bytes in 1 blocks are definitely lost in loss record 25 of 36
==4803==    at 0x4C24477: calloc (vg_replace_malloc.c:418)
==4803==    by 0x400E93: main (in /home/gerardoj/a.out)
==4803== 
==4803== LEAK SUMMARY:
==4803==    definitely lost: 72 bytes in 1 blocks
==4803==    indirectly lost: 977 bytes in 10 blocks
==4803==      possibly lost: 0 bytes in 0 blocks
==4803==    still reachable: 64,942 bytes in 262 blocks

Исходный код:

#include <curses.h>
#include <menu.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD   4

char *choices[] = {
    "Choice 1",
    "Choice 2",
    "Choice 3",
    "Choice 4",
    "Choice 5",
    "Choice 6",
    "Choice 7",
    "Exit",
}
;

int main()
{
    ITEM **my_items;
    int c;
    MENU *my_menu;
    int n_choices, i;
    ITEM *cur_item;

    /* Initialize curses */
    initscr();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);

    /* Initialize items */
    n_choices = ARRAY_SIZE(choices);
    my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
    for (i = 0; i < n_choices; ++i) {
        my_items[i] = new_item(choices[i], choices[i]);
    }
    my_items[n_choices] = (ITEM *)NULL;

    my_menu = new_menu((ITEM **)my_items);

    /* Make the menu multi valued */
    menu_opts_off(my_menu, O_ONEVALUE);

    mvprintw(LINES - 3, 0, "Use <SPACE> to select or unselect an item.");
    mvprintw(LINES - 2, 0, "<ENTER> to see presently selected items(F1 to Exit)");
    post_menu(my_menu);
    refresh();

    while ((c = getch()) != KEY_F(1)) {
        switch (c) {
        case KEY_DOWN:
            menu_driver(my_menu, REQ_DOWN_ITEM);
            break;
        case KEY_UP:
            menu_driver(my_menu, REQ_UP_ITEM);
            break;
        case ' ':
            menu_driver(my_menu, REQ_TOGGLE_ITEM);
            break;
        case 10:
            {
                char temp[200];
                ITEM **items;

                items = menu_items(my_menu);
                temp[0] = '\0';
                for (i = 0; i < item_count(my_menu); ++i)
                if(item_value(items[i]) == TRUE) {
                    strcat(temp, item_name(items[i]));
                    strcat(temp, " ");
                }
                move(20, 0);
                clrtoeol();
                mvprintw(20, 0, temp);
                refresh();
            }
            break;
        }
    }
    unpost_menu(menu);
    free_item(my_items[0]);
    free_item(my_items[1]);
    free_item(my_items[2]);
    free_item(my_items[3]);
    free_item(my_items[4]);
    free_item(my_items[5]);
    free_item(my_items[6]);
    free_item(my_items[7]);
    free_menu(my_menu);
    endwin();
}

person Mike    schedule 27.05.2010    source источник
comment
Вы скомпилировали с -g? Вы должны сделать это.   -  person user172818    schedule 27.05.2010
comment
Я сделал, но продолжаю получать ту же ошибку. gcc -g -lcurses -lmenu sample.c   -  person Mike    schedule 27.05.2010
comment
-g позволит valgrind показать вам, где именно находятся утечки, поскольку он сможет ссылаться на символы / номера строк / и т. д.   -  person Tim Post♦    schedule 27.05.2010


Ответы (3)


Согласно Руководству по программированию NCURSES, для использования библиотеки меню требуется следующее: шаги:

  • Инициализировать проклятия
  • Создавайте элементы с помощью new_item(). Вы можете указать имя и описание элементов.
  • Создайте меню с помощью new_menu(), указав элементы, которые будут присоединены.
  • Опубликуйте меню с помощью menu_post() и обновите экран.
  • Обрабатывайте пользовательские запросы в цикле и выполняйте необходимые обновления меню с помощью menu_driver.
  • Отменить публикацию меню с помощью menu_unpost()
  • Освободите память, выделенную для меню, с помощью free_menu()
  • Освободите память, выделенную для элементов, с помощью free_item()
  • End curses
  • Из того, что я могу сказать из вашего кода:

    • You don't unpost the menu (which might cause a leak, or it might just risk garbling the screen).
    • Меню освобождается после освобождения элементов (что, я думаю, может быть проблемой, а может и не быть, в зависимости от того, как реализован ncurses).
    • Освобождаются только элементы 0 и 1 из 8-элементного массива элементов. Вероятно, это утечка.
    • Массив указателей my_items никогда не освобождается. Это конечно утечка.

    Как сказал @lh3, компиляция с параметром -g позволит Valgrind указать номер строки потерянной памяти.

    Изменить (в ответ на ваш комментарий): my_items — это динамически выделяемый массив указателей на динамически создаваемые пункты меню. Другими словами, у вас есть один блок динамической памяти, и он содержит кучу указателей на кучу динамически размещаемых структур ncurses (пунктов меню). Итак, чтобы очистить, как только вы закончите, вам нужно освободить каждую из динамически выделенных структур ncurses, а затем вам нужно освободить блок памяти, который содержал указатели на эти структуры.

    Другими словами, каждому calloc или malloc нужен free, каждому new_item нужен free_item и так далее.

    for (i = 0; i < n_choices; ++i) {
        free_item(my_items[i]);
    }
    free(my_items);
    
    person Josh Kelley    schedule 27.05.2010
    comment
    не могли бы вы рассказать немного больше о том, что массив указателей my_items никогда не освобождается - person Mike; 27.05.2010
    comment
    Похоже, мы оба жертвы одного и того же избирателя. - person Tim Post♦; 27.05.2010
    comment
    Интересно, что бесплатно(my_items); не упоминается в документах ncurses. Но когда я добавляю это, количество определенно потерянных байтов изменяется со 184 до 96. Спасибо, @josh-kelly. - person andy5995; 25.02.2021

    Обратите внимание на Valgrind (это часто встречается в списке рассылки пользователей Valgrind). ):

    still reachable: 64,942 bytes in 262 blocks
    

    Это просто ссылки на блоки, которые все еще были доступны в main() при выходе, которые (в любом современном ядре) в любом случае были бы просто восстановлены ОС.

    Хотя хорошей практикой является явный free() каждый отдельный выделенный блок перед вызовом выхода, это не technically утечка памяти, поскольку она все еще может быть достигнута во время выхода.

    Сосредоточьтесь на прямо, косвенно и, возможно, потерянных блоках, как предложил Джош Келли. . Это всего лишь дополнение к ответам, которые уже указывали на вероятные источники утечек.

    person Tim Post♦    schedule 27.05.2010

    попробуйте запустить valgrind с параметром --leak-check=full, может быть?

    person mportiz08    schedule 27.05.2010