释放在简单链表中分配的内存 - C
Freeing memory allocated in simple linked list - C
我在释放在 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)
在函数insert
中,你分配了两次结构:
node_t *item = malloc(sizeof(node_t));
item = malloc(sizeof(node_t));
当您用下一个分配覆盖 item
中的指针时,第一个丢失。
此外,您没有初始化新分配节点的 next
成员,这会导致 valgrind 在取消引用未初始化的数据时发出警告。
在你的 insert() 中:
// Create new node to insert
node_t *item = malloc(sizeof(node_t));
item = malloc(sizeof(node_t));
您在 item
上两次调用 malloc(),导致分配的第一块内存永远丢失..
我的意思是你现在还没有跟踪那个内存块,所以在你的程序结束时,这是释放内存的最后机会你没有这样做,导致内存被分配,但从未被释放!
假设 OS 处理您计算机的所有内存。您的程序开始运行并请求一些内存。 OS 为您提供指向它为您分配的那块内存的指针(当您不再需要它时必须释放它)。该指针具有我们先验未知的值。
第二次调用 malloc()
并将它的 return 值赋值给 item
时,你所做的就是覆盖那个唯一的指针值(你创建的内存地址)被赋予)并得到一个新地址,指向新分配的内存。
所以现在你请求了两个内存块,但是你只知道第二个内存块的地址。那么当你不知道它的地址时,你怎么能释放第一块内存呢?
你不能!这是错误的,因为它会导致内存泄漏,如您所示。
打个比方,记忆就是一个剧场(就像Epidaurus),每个座位都是一个记忆细胞。 OS 是剧院的座位经理。你请求一个座位,经理给你一个他分配给你的座位的 ID,然后你把它写到电子笔记中。
然后你再要一个位子,经理看剧场还有没有位子,有,就给了一个位子,当然还有对应的ID。您错误地把它写在了您之前写过 ID 的电子纸条上。
所以现在你只知道第二个 ID,也就是第二个座位而不是第一个 ID,因此你不知道那个座位在哪里(剧院很大,你不太了解)。
但是,这不是您唯一的问题,当您 "create" item
时,您分配了一个值,但没有为 next
分配任何值。继续做:
item->value = num;
item->next = NULL;
完成所有这些之后,您应该能够生成以下输出:
C02QT2UBFVH6-lm:~ gsamaras$ gcc -Wall main.c
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
0
1
每个节点有两个 malloc。你只想要一个。您可以将其保留在声明中并删除以下复制 malloc 并覆盖第一个的行。
虽然您可以像您所做的那样在 insert
之外自由分配 head
,但您通常会传递 head
的 地址] 改为 insert
,以便 head
可以在 insert
中分配和赋值,并保留指针值并对 main
.
可见
基本问题。当您将指针传递给函数时,该函数会收到 指针的副本 以及它自己的(和 非常不同的 )地址。为了分配第一个节点(如head
),必须通过的地址head
进行插入,否则,您分配内存并将内存块的地址分配给 head
的 insert
副本,并在 return 上分配给 main
, head
的原始指针仍然是 NULL
.
传递 head
地址的 insert
的原型将变成,例如
void insert (node_t **head, int num)
你会从 main
调用 insert
为:
insert (&head, tmp);
(这只是第一个节点的问题,因为在分配head
之后,insert
接收到的指针的副本可能有自己的地址,但它包含的是完全相同的main
或 insert
)
中第一个节点的地址
如果您正在为函数中的第一个节点分配并且您没有在调用方中return分配节点地址,您通常希望传递[=的地址93=] 列表到您的 insert
函数。
其他问题
将值插入链表时,您必须处理两个 条件。 (如果你想优化掉对循环的不必要调用,实际上是 3)。这些是 第一个节点 的插入和 所有其他节点 的插入。 (您可以检查第二个节点的插入,因为在这种情况下不需要设置节点遍历)。要处理所有这三种情况,您的 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;
}
}
你的 free 也有点尴尬,因为你经常检查 ->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
)
将所有部分放在一起,您可以执行类似以下操作来解决问题:
#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 *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;
}
}
int main (void) {
int tmp;
node_t *head = NULL, *iter = NULL;
/* Insert nodes to end of list (from stdin) */
while (scanf (" %d", &tmp) == 1)
insert (&head, tmp);
/* Traverse list and print out all node values */
iter = head;
while (iter) {
printf ("%d\n", iter->value);
iter = iter->next;
}
/* Free the list */
iter = head;
while (iter->next) {
node_t *victim = iter;
iter = iter->next;
free (victim);
}
if (iter) free (iter);
return 0;
}
示例输入数据
$ cat ../dat/10int_nl.txt
8572
-2213
6434
16330
3034
12346
4855
16985
11250
1495
例子Use/Output
$ valgrind ./bin/llvalgrind <../dat/10int_nl.txt
==31935== Memcheck, a memory error detector
==31935== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==31935== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==31935== Command: ./bin/llvalgrind
==31935==
8572
-2213
6434
16330
3034
12346
4855
16985
11250
1495
==31935==
==31935== HEAP SUMMARY:
==31935== in use at exit: 0 bytes in 0 blocks
==31935== total heap usage: 10 allocs, 10 frees, 160 bytes allocated
==31935==
==31935== All heap blocks were freed -- no leaks are possible
==31935==
==31935== For counts of detected and suppressed errors, rerun with: -v
==31935== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
查看所有解决方案,如果您有任何问题,请告诉我。
我在释放在 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)
在函数insert
中,你分配了两次结构:
node_t *item = malloc(sizeof(node_t));
item = malloc(sizeof(node_t));
当您用下一个分配覆盖 item
中的指针时,第一个丢失。
此外,您没有初始化新分配节点的 next
成员,这会导致 valgrind 在取消引用未初始化的数据时发出警告。
在你的 insert() 中:
// Create new node to insert
node_t *item = malloc(sizeof(node_t));
item = malloc(sizeof(node_t));
您在 item
上两次调用 malloc(),导致分配的第一块内存永远丢失..
我的意思是你现在还没有跟踪那个内存块,所以在你的程序结束时,这是释放内存的最后机会你没有这样做,导致内存被分配,但从未被释放!
假设 OS 处理您计算机的所有内存。您的程序开始运行并请求一些内存。 OS 为您提供指向它为您分配的那块内存的指针(当您不再需要它时必须释放它)。该指针具有我们先验未知的值。
第二次调用 malloc()
并将它的 return 值赋值给 item
时,你所做的就是覆盖那个唯一的指针值(你创建的内存地址)被赋予)并得到一个新地址,指向新分配的内存。
所以现在你请求了两个内存块,但是你只知道第二个内存块的地址。那么当你不知道它的地址时,你怎么能释放第一块内存呢?
你不能!这是错误的,因为它会导致内存泄漏,如您所示。
打个比方,记忆就是一个剧场(就像Epidaurus),每个座位都是一个记忆细胞。 OS 是剧院的座位经理。你请求一个座位,经理给你一个他分配给你的座位的 ID,然后你把它写到电子笔记中。 然后你再要一个位子,经理看剧场还有没有位子,有,就给了一个位子,当然还有对应的ID。您错误地把它写在了您之前写过 ID 的电子纸条上。 所以现在你只知道第二个 ID,也就是第二个座位而不是第一个 ID,因此你不知道那个座位在哪里(剧院很大,你不太了解)。
但是,这不是您唯一的问题,当您 "create" item
时,您分配了一个值,但没有为 next
分配任何值。继续做:
item->value = num;
item->next = NULL;
完成所有这些之后,您应该能够生成以下输出:
C02QT2UBFVH6-lm:~ gsamaras$ gcc -Wall main.c
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
0
1
每个节点有两个 malloc。你只想要一个。您可以将其保留在声明中并删除以下复制 malloc 并覆盖第一个的行。
虽然您可以像您所做的那样在 insert
之外自由分配 head
,但您通常会传递 head
的 地址] 改为 insert
,以便 head
可以在 insert
中分配和赋值,并保留指针值并对 main
.
基本问题。当您将指针传递给函数时,该函数会收到 指针的副本 以及它自己的(和 非常不同的 )地址。为了分配第一个节点(如head
),必须通过的地址head
进行插入,否则,您分配内存并将内存块的地址分配给 head
的 insert
副本,并在 return 上分配给 main
, head
的原始指针仍然是 NULL
.
传递 head
地址的 insert
的原型将变成,例如
void insert (node_t **head, int num)
你会从 main
调用 insert
为:
insert (&head, tmp);
(这只是第一个节点的问题,因为在分配head
之后,insert
接收到的指针的副本可能有自己的地址,但它包含的是完全相同的main
或 insert
)
如果您正在为函数中的第一个节点分配并且您没有在调用方中return分配节点地址,您通常希望传递[=的地址93=] 列表到您的 insert
函数。
其他问题
将值插入链表时,您必须处理两个 条件。 (如果你想优化掉对循环的不必要调用,实际上是 3)。这些是 第一个节点 的插入和 所有其他节点 的插入。 (您可以检查第二个节点的插入,因为在这种情况下不需要设置节点遍历)。要处理所有这三种情况,您的 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;
}
}
你的 free 也有点尴尬,因为你经常检查 ->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
)
将所有部分放在一起,您可以执行类似以下操作来解决问题:
#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 *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;
}
}
int main (void) {
int tmp;
node_t *head = NULL, *iter = NULL;
/* Insert nodes to end of list (from stdin) */
while (scanf (" %d", &tmp) == 1)
insert (&head, tmp);
/* Traverse list and print out all node values */
iter = head;
while (iter) {
printf ("%d\n", iter->value);
iter = iter->next;
}
/* Free the list */
iter = head;
while (iter->next) {
node_t *victim = iter;
iter = iter->next;
free (victim);
}
if (iter) free (iter);
return 0;
}
示例输入数据
$ cat ../dat/10int_nl.txt
8572
-2213
6434
16330
3034
12346
4855
16985
11250
1495
例子Use/Output
$ valgrind ./bin/llvalgrind <../dat/10int_nl.txt
==31935== Memcheck, a memory error detector
==31935== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==31935== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==31935== Command: ./bin/llvalgrind
==31935==
8572
-2213
6434
16330
3034
12346
4855
16985
11250
1495
==31935==
==31935== HEAP SUMMARY:
==31935== in use at exit: 0 bytes in 0 blocks
==31935== total heap usage: 10 allocs, 10 frees, 160 bytes allocated
==31935==
==31935== All heap blocks were freed -- no leaks are possible
==31935==
==31935== For counts of detected and suppressed errors, rerun with: -v
==31935== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
查看所有解决方案,如果您有任何问题,请告诉我。