调用 calloc - 内存泄漏 valgrind

发布于 2024-09-03 11:09:11 字数 2947 浏览 8 评论 0原文

以下代码是 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();
}

The following code is an example from the NCURSES menu library. I'm not sure what could be wrong with the code, but valgrind reports some problems. Any ideas...

==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

Source code:

#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();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

装纯掩盖桑 2024-09-10 11:09:11

根据 NCURSES 编程指南,使用菜单库需要以下内容步骤:

  • 初始化诅咒
  • 使用 new_item() 创建项目。您可以指定项目的名称和描述。
  • 通过指定要附加的项目,使用 new_menu() 创建菜单。
  • 使用menu_post()发布菜单并刷新屏幕。
  • 使用循环处理用户请求,并使用 menu_driver 对菜单进行必要的更新。
  • 使用 menu_unpost() 取消发布菜单
  • 通过 free_menu() 释放分配给菜单的内存
  • 使用 free_item() 释放分配给项目的内存
  • 结束诅咒
  • 从我从你的代码中可以看出:

    • 你不会取消发布菜单(这可能会导致泄漏,或者可能只是冒着屏幕乱码的风险)。
    • 菜单在项目被释放后被释放(我猜这可能是也可能不是问题,具体取决于 ncurses 的实现方式)。
    • 仅释放 8 元素项目数组中的项目 0 和 1。这很可能是泄漏。
    • my_items 指针数组永远不会被释放。这肯定是泄漏。

    正如 @lh3 所说,使用 -g 选项进行编译将使 Valgrind 给出丢失内存的行号。

    编辑(响应您的评论):my_items是动态分配的指向动态创建的菜单项的指针数组。换句话说,你有一个动态内存块,它包含一堆指向一堆动态分配的 ncurses 结构(菜单项)的指针。因此,完成后要进行清理,您需要释放每个动态分配的 ncurses 结构,然后需要释放保存指向这些结构的指针的内存块。

    换句话说,每个callocmalloc都需要一个free,每个new_item需要一个free_item代码>,等等。

    for (i = 0; i < n_choices; ++i) {
        free_item(my_items[i]);
    }
    free(my_items);
    

    According to the NCURSES Programming Howto, using the menus library requires the following steps:

  • Initialize curses
  • Create items using new_item(). You can specify a name and description for the items.
  • Create the menu with new_menu() by specifying the items to be attached with.
  • Post the menu with menu_post() and refresh the screen.
  • Process the user requests with a loop and do necessary updates to menu with menu_driver.
  • Unpost the menu with menu_unpost()
  • Free the memory allocated to menu by free_menu()
  • Free the memory allocated to the items with free_item()
  • End curses

  • From what I can tell from your code:

    • You don't unpost the menu (which might cause a leak, or it might just risk garbling the screen).
    • The menu is freed after the items are freed (which I guess may or may not be a problem depending on how ncurses is implemented).
    • Only items 0 and 1 of the 8-element array of items are freed. This is probably a leak.
    • The my_items array of pointers is never freed. This is certainly a leak.

    As @lh3 said, compiling with the -g option will let Valgrind give the line number of lost memory.

    Edit (in response to your comment): my_items is a dynamically allocated array of pointers to dynamically created menu items. In other words, you have one block of dynamic memory, and it contains a bunch of pointers to a bunch of dynamically allocated ncurses structures (menu items). So, to clean up once you're done, you need to free each of the dynamically allocated ncurses structures, and then you need to free the block of memory that held the pointers to those structures.

    In other words, every calloc or malloc needs a free, every new_item needs a free_item, and so on.

    for (i = 0; i < n_choices; ++i) {
        free_item(my_items[i]);
    }
    free(my_items);
    
    风蛊 2024-09-10 11:09:11

    Valgrind 需要注意的事项(Valgrind 用户的邮件列表经常出现) ):

    still reachable: 64,942 bytes in 262 blocks
    

    这只是引用在退出时仍可在 main() 中访问的块,这些块(在任何现代内核下)无论如何都会被操作系统回收。

    虽然在调用退出之前显式地free()每个分配的块是一个很好的做法,但这在技术上并不是泄漏内存,因为在退出时仍然可以到达它。

    正如 Josh Kelly 建议的那样,重点关注直接、间接和可能丢失的块。这只是对已经指出可能的泄漏来源的答案的补充。

    Something to note with Valgrind (this comes up on the Valgrind user's mailing list often):

    still reachable: 64,942 bytes in 262 blocks
    

    This is just referencing blocks that were still reachable in main() at exit, which (under any modern kernel) would just be reclaimed by the OS anyway.

    While its good practice to explicitly free() every single allocated block before exit is called, this is not technically leaked memory, since it still could be reached at the time of exit.

    Focus on directly, indirectly and possibly lost blocks as Josh Kelly has suggested. This is just a supplement to the answers that have already pointed out the likely sources of leaks.

    习惯成性 2024-09-10 11:09:11

    尝试使用 --leak-check=full 运行 valgrind 吧?

    try running valgrind with --leak-check=full maybe?

    浅暮の光 2024-09-10 11:09:11
    free_item(my_items[7]);
    free(my_items);
    
    free_item(my_items[7]);
    free(my_items);
    
    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文