我使用链表在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;但据我所知,我的销毁功能应该抓住我分配的一切。我在这里缺少什么?
答案 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
}
祝你好运。