我在释放C中简单链接列表实现中分配的内存时遇到问题.Valgrind告诉我,我没有释放所有内容,但我无法弄清问题所在。我的代码和valgrind输出如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int value;
struct node *next;
} node_t;
void insert(node_t *head, int num) {
// Create new node to insert
node_t *item = malloc(sizeof(node_t));
item = malloc(sizeof(node_t));
if (item == NULL) {
exit(2);
}
item->value = num;
// Insert new node at end of list
node_t *cur = head;
while (cur->next != NULL) {
cur = cur->next;
}
cur->next = item;
}
int main(int argc, char *argv[]) {
// Create head or root node
node_t *head;
head = malloc(sizeof(node_t));
head->value = 0;
head->next = NULL;
// Insert nodes to end of list
insert(head, 1);
// Traverse list and print out all node values
node_t *cur = head;
while (cur != NULL) {
printf("%d\n", cur->value);
cur = cur->next;
}
// Free the list
cur = head;
node_t *previous;
while (cur != NULL) {
previous = cur;
cur = cur->next;
free(previous);
}
return 0;
}
// EOF
Valgrid显示以下错误
==9054== Memcheck, a memory error detector
==9054== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==9054== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==9054== Command: ./linkedlist
==9054==
0
1
==9054== Conditional jump or move depends on uninitialised value(s)
==9054== at 0x100000ED0: main (in ./linkedlist)
==9054== Uninitialised value was created by a heap allocation
==9054== at 0x100008EBB: malloc (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==9054== by 0x100000E03: insert (in ./linkedlist)
==9054== by 0x100000EBF: main (in ./linkedlist)
==9054==
==9054== Conditional jump or move depends on uninitialised value(s)
==9054== at 0x100000F0E: main (in ./linkedlist)
==9054== Uninitialised value was created by a heap allocation
==9054== at 0x100008EBB: malloc (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==9054== by 0x100000E03: insert (in ./linkedlist)
==9054== by 0x100000EBF: main (in ./linkedlist)
==9054==
==9054==
==9054== HEAP SUMMARY:
==9054== in use at exit: 26,456 bytes in 193 blocks
==9054== total heap usage: 274 allocs, 81 frees, 32,608 bytes allocated
==9054==
==9054== 16 bytes in 1 blocks are definitely lost in loss record 5 of 65
==9054== at 0x100008EBB: malloc (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==9054== by 0x100000DF0: insert (in ./linkedlist)
==9054== by 0x100000EBF: main (in ./linkedlist)
==9054==
==9054== LEAK SUMMARY:
==9054== definitely lost: 16 bytes in 1 blocks
==9054== indirectly lost: 0 bytes in 0 blocks
==9054== possibly lost: 0 bytes in 0 blocks
==9054== still reachable: 0 bytes in 0 blocks
==9054== suppressed: 26,440 bytes in 192 blocks
==9054==
==9054== For counts of detected and suppressed errors, rerun with: -v
==9054== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 18 from 18)
答案 0 :(得分:4)
在insert()中:
// Create new node to insert
node_t *item = malloc(sizeof(node_t));
item = malloc(sizeof(node_t));
你在item
上调用了malloc()两次,导致分配的第一块内存永远丢失..
我的意思是你现在没有跟踪那个内存块,所以在你的程序结束时,这是释放内存的最后一次机会你不是这样,会导致内存被分配,但从未被释放!
想象一下,操作系统会处理您计算机的所有内存。你的程序发挥作用,并要求一些记忆。操作系统为您提供指向它为您分配的内存的指针(当您不再需要它时必须释放它)。该指针有一个我们不知道apriori的值。
第二次调用malloc()
并将其返回值分配给item
时,您要做的是覆盖该唯一指针值(您给出的内存地址)并得到一个新地址,指向新分配的内存。
所以现在你请求了两个内存块,但是你只知道第二个内存块的地址。那么当你不知道它的地址时,如何释放第一块内存呢?
你不能!这是错误的,因为它会导致内存泄漏,如你所示。
类比是内存是一个剧院(如Epidaurus),每个座位都是一个记忆单元。操作系统是剧院的席位经理。您申请一个座位,经理会给您一个他分配给您的座位的ID,然后您将其写入电子票据。 然后你再请一个座位,经理检查剧院里是否还有座位,是的,所以他给了一个席位,当然还有一个相应的身份证。你错误地把它写在你写过以前ID的电子笔记上。 所以现在你只知道第二个ID,因此是第二个座位,而不是第一个ID,因此你不知道那个座位在哪里(剧院很大,你对此并不了解)。 子>
但是,这不是您唯一的问题,当您“创建”item
时,您指定了一个值,但您没有为next
分配内容。继续做:
item->value = num;
item->next = NULL;
毕竟,你应该能够产生以下输出:
C02QT2UBFVH6-lm:~ gsamaras$ gcc -Wall main.c
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
0
1
答案 1 :(得分:2)
在函数insert
中,您将结构分配两次:
node_t *item = malloc(sizeof(node_t));
item = malloc(sizeof(node_t));
当您使用下一个分配覆盖item
中的指针时,第一个丢失。
此外,您不初始化新分配的节点的next
成员,这会导致valgrind发出警告,因为您要取消引用未初始化的数据。
答案 2 :(得分:2)
虽然您可以像在head
之外那样免费分配insert
,但通常会将 head
的地址传递给{{1}相反,以便可以在insert
中分配和分配head
,并使指针值保留并显示给insert
。
基本问题。当您将指针传递给函数时,该函数会接收指针的副本及其自己的(和非常不同的)地址。为了分配第一个节点(例如main
),您必须将 head
的地址传递给插入,否则您将分配内存并分配内存块的地址为head
中 head
的副本,返回insert
时,main
的原始指针仍为{{} 1}}。
传递head
地址的NULL
原型将成为,例如
insert
您可以从head
致电void insert (node_t **head, int num)
作为:
insert
(这只是第一个节点的问题,因为在分配main
之后, insert (&head, tmp);
收到的指针副本可能有自己的地址,但它包含的地址完全相同head
或insert
)
如果要为函数中的第一个节点分配并且没有在调用者中返回节点地址以进行赋值,则通常需要将地址列表传递给{{1功能。
其他问题
将值插入链接列表时,您必须处理两个条件。 (实际上,如果你想优化对循环的不必要的调用,则为3)。这些是插入第一个节点并插入所有其他节点。 (您可以检查第二个节点的插入,因为在这种情况下不需要设置遍历节点)。要处理所有这三种情况,您的main
将如下所示:
insert
你的自由同样有点尴尬,因为你通常会检查insert
是否被分配来做出关于当前节点的决定,然后清理循环外的落后者。 (这在很大程度上取决于您,我只是在下面提供替代方案,我的insert
是您的void insert (node_t **head, int num)
{
/* Create new node to insert */
node_t *node = calloc (1, sizeof *node);
if (node == NULL) {
fprintf (stderr, "error: virtual memory exhusted.\n");
exit (2);
}
node->value = num;
node->next = NULL;
/* Insert node at end */
if (!(*head)) /* handle first node */
*head = node;
else if (!(*head)->next) /* handle second */
(*head)->next = node;
else { /* handle remaining */
node_t *cur = *head;
while (cur->next)
cur = cur->next;
cur->next = node;
}
}
而我的->next
是您的iter
)
cur
最后,对于victim
,您在使用previous
时可能会遇到有关基于未初始值的跳转的错误(您的选项是使用 /* Free the list */
iter = head;
while (iter->next) {
node_t *victim = iter;
iter = iter->next;
free (victim);
}
if (iter) free (iter);
然后使用valgrind
,或者只需使用malloc
- 它将新内存分配并设置为malloc
)
将所有部分组合在一起,您可以执行以下操作来解决问题:
memset
示例输入数据
calloc
示例使用/输出
0/NULL
查看所有解决方案,如果您有任何疑问,请与我们联系。
答案 3 :(得分:1)
每个节点都有两个 malloc。你只想要一个。您可以在声明中保留一个,并删除与malloc重复的以下行并覆盖第一行。