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

标签 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 运行它会产生以下已识别的未发布 block :

(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)

它们似乎都源于“create_stack”,但据我所知,我的销毁函数应该捕获我分配的所有内容。我在这里缺少什么?

最佳答案

您的销毁逻辑试图在一个函数中递归地合并两个任务(删除堆栈对象及其项)。更糟糕的是,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
}

祝你好运。

关于c - Valgrind 检测到可到达的泄漏,它们在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31738264/

相关文章:

c - 返回数组指针产生 "warning: return from incompatible pointer type"

java - 理解节点和链表概念的问题

c - 从C中的文件中读取链表

node.js - Nodejs Websocket内存泄漏

c - 从链表中删除节点

c - 遍历数组并将元素移动到数组末尾

c - 翻译 curl -v --data 命令

c - 如何使该代码工作 (isdigit)

objective-c - 我找不到这个内存泄漏。我以为我正在正确地释放一切

PHP:对象数组和内存泄漏