C - 双向链表释放内存瓦尔格林德错误



嗨,我正在尝试释放我在双向链表中分配的内存,但是当我使用 valgrind 检查它时,我在 free_all 函数中出现了一些错误(我认为(,但我不知道如何避免它。

我认为在free_all函数中,我错误地使用了临时和节点指针,或者我需要先分配它,然后再使用它们,但是当我尝试这种方法时,valgrind 仍然给了我一些错误。

#include <stdio.h>
#include <stdlib.h>
/*
to compile it:
gcc -g -Wall -ggdb3  double_linkedlist2.c -o double_linkedlist
to check for memory leak and error:
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt ./double_linkedlist
*/
typedef struct listitem
{
struct listitem *next;    // pointer to next item
struct listitem *prev;    // pointer to previous item
int data;                 // some data
} ITEM;

int main (void)
{
// prototype functions
void free_all (ITEM *lst_ptr);

// Variables
ITEM *p_temp, *head;
head = malloc (sizeof (ITEM));  // head will keep first and last element in its pointers
head -> next = head;            // the last element in the list (at first head -> next and head -> prev will point to the head)
head -> prev = head;            // the first element in the list

for (int i = 0; i < 3; i++)
{
p_temp = malloc (sizeof (ITEM));     // allocate some memory for the new list item
p_temp -> data = i;                  // set the list item's data to the loop count so that we can see where it is in the list
p_temp -> next = head -> next;          // this will insert at the FRONT of the list
head -> next = p_temp;                  // and set the list head to the newly created list item
p_temp -> prev = head;              // this will insert at the BACK of the list
p_temp -> next -> prev = p_temp;       // and set the list 'tail' to the newly created item
}
// now let's see what we got going forward
printf ("Going forwardn");
p_temp = head -> next;
while (p_temp != head)
{
printf ("forward list item: current is %p; next is %p; prev is %p; data is %dn", p_temp, p_temp -> next, p_temp -> prev, p_temp -> data);
p_temp = p_temp -> next;
}
// now let's see what we got going backward
printf ("Going backwardsn");
p_temp = head -> prev;
while (p_temp != head)
{
printf ("backward list item; current is %p; next is %p; prev is %p; data is %dn", p_temp, p_temp -> next, p_temp -> prev, p_temp -> data);
p_temp = p_temp -> prev;
}
printf ("n");
free_all (head);
return 0;
}
void free_all (ITEM *head)
{
ITEM *temp, *node;
node = head;
while (node != head -> prev)
{
temp = node;
printf ("freed list item: current is %p; next is %p; prev is %p; data is %dn", temp, temp -> next, temp -> prev, temp -> data);
node = node -> next;
free (temp);
}
free (node);
free (head);
}

您的free_all至少有两个错误: while 条件引用 head->prev,但在第一次迭代中,您可以释放头部(释放后使用(。 退出循环后,尽管在第一次迭代中释放了它,但您仍释放了头部。 free_all(( 确实适用于单元素情况。

此修改后,valgrind 中没有错误或内存泄漏

void free_all (ITEM *head)
{
ITEM *temp, *node = NULL;
node = head -> next;
while (node != head -> prev)
{
temp = node;
printf ("freed list item: current is %p; next is %p; prev is %p; data is %dn", node, node -> next, node -> prev, node -> data);
node = node -> next;
free (temp);
}
node = head -> prev;
printf ("freed list item: current is %p; next is %p; prev is %p; data is %dn", node, node -> next, node -> prev, node -> data);
free (head);
free (node);
}

这篇文章是由mevets写的,作为对我的解决方案的编辑,但我认为最好也将其包含在线程中:

梅维茨:

我会倾向于这样的东西:

void Unlink(ITEM **head, ITEM *t) {
if (t->next == t) {
/* remove head */
*head = NULL;
} else {
t->prev->next = t->next;
t->next->prev = t->prev;
if (t == *head) {
*head = t->next;
}
}
}
/*
remove and return the element after head
*/
ITEM *Pop(ITEM **head) {
ITEM *node;
if ((node = *head) != NULL) {
node = node->next;
Unlink(head, node);
}
return node;
}

void free_all (ITEM *head) {
ITEM *node;
while ((node = Pop(&head)) != NULL) {
free(node);
}
}

它将列表维护(取消链接(与排序(Pop(和内存管理(free_all(分开。这给你留下了更多的边界,你可以在其中对列表做出断言,对于考试前和后取消链接,列表应该是有效的,可以检查。此外,如果存在对列表的并发访问,则可以将 Pop(( 括起来并带有锁,以最大程度地减少冲突。

缓存分配器本身就是一个东西,但合约的典型部分是你释放处于已知状态的节点,这样当它们被重新分配时,它可以跳过它们的构造。

最新更新