简体   繁体   English

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

[英]Valgrind detects reachable leaks, where are they?

I have written a small implementation of a simple stack in C using a linked list. 我使用链表在C中编写了一个简单堆栈的小型实现。 The application works after a bit of fussing with gdb and valgrind. 该应用程序在对gdb和valgrind进行了一些大惊小怪之后才能正常工作。 However, I have noticed that valgrind reports a number of "still reachable" leaks at termination of the program. 但是,我注意到valgrind在程序终止时报告了许多“仍可到达”的泄漏。

After some googling, I have found that these type of leaks are not an issue and that I generally shouldn't worry about them. 经过一番谷歌搜索后,我发现这些类型的泄漏不是问题,我通常不必担心它们。 Well that's great news! 好吧,这是个好消息! The only thing is, I would REALLY like to understand why valgrind sees these leaks and where they appear. 唯一的是,我真的很想了解valgrind为什么会看到这些泄漏及其出现的位置。 I went to great pains through my first pass at this application to diligently release any allocated memory. 在我第一次通过此应用程序时,我非常努力地努力释放所有分配的内存。 What did I miss? 我错过了什么? Where are these allocations that valgrind sees but I cannot? valgrind看到这些分配在哪里,但我看不到?

I have been staring at this for awhile now, and despite fiddling with the code this way and that, have come up empty handed. 我已经凝视了一段时间,尽管以这种方式摆弄代码,却空手而归。 Hopefully you all can help me in this endeavor. 希望大家能在这项工作中对我有所帮助。

I have stripped the app down to a minimal example that will present the leak I am observing. 我已将应用程序简化为一个最小的示例,该示例将显示我正在观察的泄漏。 It just initializes the stack and then immediately destroys it and exits. 它只是初始化堆栈,然后立即销毁它并退出。

// 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;
}

Running this through valgrind produces the following identified, unreleased blocks: 通过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)

They all seem to stem from "create_stack" but as far as I can tell my destroy function should be catching everything I allocate. 它们似乎都源于“ create_stack”,但是据我所知,destroy函数应该捕获我分配的所有内容。 What am I missing here? 我在这里想念什么?

Your destruction logic is trying to recursively combine two tasks (deleting the stack object and its items) in a single function. 您的销毁逻辑试图将两个任务(删除堆栈对象及其项)以递归方式组合在一个函数中。 Worse, the else state shouldn't be there at all . 更糟的是, else国家不应该存在 You already determined you're sitting on a dynamic node ( recurse ). 您已经确定要坐在动态节点上( recurse )。 It needs to be deleted. 需要删除。 Whether it has a next or not is not for the current depth to decide; 是否有next不是由当前深度决定的。 that will be taken care of in the next depth of recursion. 在下一个递归深度中将予以解决。

Honestly, this is much simpler if you create a helper to wipe out the linked list, and as separate wrapper to do said-same to the stack itself: 坦白地说,如果您创建一个帮助程序来擦除链表,并作为单独的包装器来进行上述操作(与堆栈本身相同),则这要简单得多:

/*--- 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
}

and simply invoked as: 并简单地调用为:

destroy_stack(my_stack);

Personally I would do it iteratively (thus remove the helper in the process), but I'm sure you have your reasons for otherwise. 就我个人而言,我会反复进行此操作(因此在此过程中删除帮助程序),但是我确定您有其他理由。 For example: 例如:

/*--- 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
}

Best of luck. 祝你好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM