Valgrind检测到可到达的泄漏,它们在哪里?

时间:2015-07-31 04:48:41

标签: c memory-leaks linked-list stack valgrind

我使用链表在C中编写了一个简单堆栈的小实现。该应用程序在与gdb和valgrind有点混乱之后工作。但是,我注意到valgrind报告了一些"仍然可以访问"在程序结束时泄漏。

经过一些谷歌搜索,我发现这些类型的泄漏不是问题,我通常不应该担心它们。那个好消息!唯一的问题是,我真的很想理解为什么valgrind会看到这些泄漏以及它们出现在哪里。通过我在这个应用程序的第一次通过,我努力释放任何分配的内存,我付出了巨大的努力。我错过了什么? valgrind看到的这些分配在哪里,但我不能?

我现在已经盯着这一段了一段时间,尽管这种方式摆弄了代码而且空手而归。希望你们都能帮助我完成这项工作。

我已将应用程序剥离到最小的示例,该示例将显示我正在观察的泄漏。它只是初始化堆栈,然后立即销毁它并退出。

// simple stack implementation
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

/*--- data structure in a stack item ---*/
struct data {
    char * message;
};

/*--- stack item structure ---*/
struct item {
    struct data * my_data;
    struct item * next;
};

/*--- stack data structure ---*/
struct stack {
    int initialized;
    int empty;
    struct item * head;
};
struct stack * my_stack;

/*--- create a new stack data structure with provided initial data ---*/
int
create_stack(struct data  ** initial_items, int num_items) {

    //allocate memory for the stack structure itself
    my_stack = (struct stack *) malloc(sizeof(struct stack));
    if(!my_stack) return -1;
    my_stack->empty = 1;
    my_stack ->initialized = 0;

    if(num_items) {
        //allocate memory for the head of the list first
        my_stack->head = (struct item *) malloc(sizeof(struct item));
        if(!my_stack->head) return -1;
        my_stack->head->my_data = initial_items[0];
        my_stack->head->next = NULL;
        struct item * tracker = my_stack->head;

        //fill the stack with elements containing the provided data
        int i = 1;
        for(; i < (num_items); i++) {
            tracker->next = (struct item *) malloc(sizeof(struct item));
            tracker = tracker->next;
            if(!tracker) return -1;
            tracker->my_data = initial_items[i];
            tracker->next = NULL;
        }

        //no longer empty
        my_stack->empty = 0;
    }

    //set initialized flag & return
    my_stack->initialized = 1;
    return 0;
}

/*--- destroy the stack by recursively freeing nodes ---*/
int
destroy_stack(struct item * recurse) {

    //if the starting node is valid, begin
    if(recurse) {
        //if the current node links to another
        if(recurse->next)
            //recurse on the next node
            destroy_stack(recurse->next);
        else
            //otherwise we hit the end, free the node
            free(recurse);
    }
    //the starting node is invalid, just free the stack itself
    else {
        free(my_stack);
    }

    return 0;
}

/*--- test wrapper ---*/
int
main(int argc, char **argv) {

    //initialize 10 element list of data structures to fill the stack items with
    int i = 0;
    int length = 10;
    struct data ** initial_values = (struct data **) malloc(length*sizeof(struct data *));

    //set each data element's value to i
    for(; i < length; i++) {
        char temp[16];
        sprintf(temp, "%d", i);
        initial_values[i] = (struct data *) malloc(sizeof(struct data));
        initial_values[i]->message = strdup(temp);
    }

    //simple test case
    //create a stack with the generated initial data, then destroy it
    assert( !create_stack(initial_values, length) );
    assert( !destroy_stack(my_stack->head) );

    //free the string data our stack nodes were pointing to
    i = 0;
    while(i < length) {
        free(initial_values[i]->message);
        free(initial_values[i]);
        i += 1;
    }
    free(initial_values);

    return 0;
}

通过valgrind运行它会生成以下已识别的未发布块:

(trusty)kin3tix@localhost:~/C Practice/Data Structures$ valgrind --leak-check=full --show-leak-kinds=all ./stack_leak_test
==19340== Memcheck, a memory error detector
==19340== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==19340== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==19340== Command: ./stack_leak_test
==19340== 
==19340== 
==19340== HEAP SUMMARY:
==19340==     in use at exit: 168 bytes in 10 blocks
==19340==   total heap usage: 32 allocs, 22 frees, 364 bytes allocated
==19340== 
==19340== 16 bytes in 1 blocks are still reachable in loss record 1 of 3
==19340==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==19340==    by 0x400739: create_stack (stack_leak_test.c:40)
==19340==    by 0x40093B: main (stack_leak_test.c:95)
==19340== 
==19340== 24 bytes in 1 blocks are still reachable in loss record 2 of 3
==19340==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==19340==    by 0x4006E6: create_stack (stack_leak_test.c:33)
==19340==    by 0x40093B: main (stack_leak_test.c:95)
==19340== 
==19340== 128 bytes in 8 blocks are still reachable in loss record 3 of 3
==19340==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==19340==    by 0x4007A1: create_stack (stack_leak_test.c:48)
==19340==    by 0x40093B: main (stack_leak_test.c:95)
==19340== 
==19340== LEAK SUMMARY:
==19340==    definitely lost: 0 bytes in 0 blocks
==19340==    indirectly lost: 0 bytes in 0 blocks
==19340==      possibly lost: 0 bytes in 0 blocks
==19340==    still reachable: 168 bytes in 10 blocks
==19340==         suppressed: 0 bytes in 0 blocks
==19340== 
==19340== For counts of detected and suppressed errors, rerun with: -v
==19340== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

他们似乎都源于&#34; create_stack&#34;但据我所知,我的销毁功能应该抓住我分配的一切。我在这里缺少什么?

1 个答案:

答案 0 :(得分:2)

您的销毁逻辑正试图在一个函数中递归地组合两个任务(删除堆栈对象及其项目)。更糟糕的是,else州不应该在所有。您已经确定您正坐在动态节点(recurse)上。它需要删除。是否有next不是由当前的深度来决定的;这将在下一个递归深度中得到处理。

老实说,如果您创建一个帮助程序来清除链接列表,并且作为单独的包装程序来执行上述操作,这会更简单:

/*--- destroy linked list nodes recursively ---*/
static void destroy_stack_items(struct item *item)
{
    if (item)
    {
        destroy_stack_items(item->next);
        free(item);
    }
}

/*--- destroy the stack by recursively freeing nodes ---*/
int destroy_stack(struct stack *s)
{
    if (s)
    {
        destroy_stack_items(s->head);
        free(s);
    }
    return 0
}

并简单地调用为:

destroy_stack(my_stack);

就我个人而言,我会迭代地进行(因此在此过程中删除帮助者),但我确定你有其他理由。例如:

/*--- destroy the stack by iteratively freeing nodes ---*/
int destroy_stack(struct stack *s)
{
    if (s)
    {
        while (s->head)
        {
            struct item *tmp = s->head;
            s->head = tmp->next;
            free(tmp);
        }
        free(s);
    }
    return 0
}

祝你好运。