释放在简单链表中分配的内存 - 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进行插入,否则,您分配内存并将内存块的地址分配给 headinsert 副本,并在 return 上分配给 main , head 的原始指针仍然是 NULL.

传递 head 地址的 insert 的原型将变成,例如

void insert (node_t **head, int num)

你会从 main 调用 insert 为:

    insert (&head, tmp);

(这只是第一个节点的问题,因为在分配head之后,insert接收到的指针的副本可能有自己的地址,但它包含的是完全相同的maininsert)

中第一个节点的地址

如果您正在为函数中的第一个节点分配并且您没有在调用方中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)

查看所有解决方案,如果您有任何问题,请告诉我。