释放数据结构的错误方法

时间:2019-07-04 11:43:33

标签: c data-structures memory-management linked-list free

我在大学里参加过C编程考试,该考试中的一个问题是如何释放链表数据结构。

我的方法是释放每个节点的数据,但是没有充分的理由,我没有得到该练习的要点。

这是我未接受的代码:

#include <stdlib.h>

struct node {
  int value;
  struct node *next;
};

void free_list(struct node *head) {
  for (struct node *p = head; p != NULL; p = p->next) {
    free(p);
  }
}

有人可以解释一下这是怎么回事,以及从内存中释放链接列表的正确方法是什么?

4 个答案:

答案 0 :(得分:4)

for循环的最后一部分是在{em>之后读取p->next

您需要缓冲指针:

free(p);

答案 1 :(得分:2)

Sander de Dycker 的评论很棒,

花些时间,您会发现:

p在执行p->next之前被释放,因此p->next读取已经释放的内存。

如果您正在寻找一种使用for loop释放链接列表的好方法,请尝试以下方法:

#include <stdlib.h>

struct node {
  int value;
  struct node *next;
};

void free_list(struct node *head) {
  struct node *q;
  for (struct node *p = head; p != NULL; p = q) {
    q = p->next;
    free(p);
  }
}

此错误记录在C Bible by Dennis Ritchie

答案 2 :(得分:2)

放松打在头上的指甲。使用描述性名称使其更加明显。您要确保保存要释放的指针,然后之前前进到列表中的下一个节点,例如,在保存的指针上调用free,例如

void free_list(struct node *head) 
{
    while (head) {
        struct node *victim = head;     /* save node to free */
        head = head->next;              /* advance to next before free */
        free (victim);                  /* free node */
    }
}

答案 3 :(得分:-1)

  

有人可以解释这是怎么回事

在节点上调用free后,您将无法访问head->next。您必须先免费head->next head

  

什么是从内存中释放链表的正确方法?

一个很好的解决方案是递归。递归有助于使代码易于理解,但是除非您意识到危险,否则请在生产代码中避免使用。我经常将其用于原型制作。这是一个例子。

void free_list(struct node *head) 
{
    if(head) {
        // Before freeing the first element, free the rest
        free_list(head->next)     
        // Now it's only one element left to free
        free(head);
    }
}

它像这样工作。假设我们有一个列表:[e0, e1, e2, ..., en]。我们要释放所有元素。为此,我们首先释放列表[e1, e2, ..., en],然后释放元素e0。下一步以相同的方式工作。我们要释放[e1, e2, ... en],因此我们从调用列表[e2, e3, ..., en]上的相同算法开始,完成后,我们释放e1。基本情况是当我们得到一个空列表[]时,我们什么也不做。

您也可以非递归地执行此操作。它虽然不那么漂亮,但通常效率更高,并且消除了堆栈溢出的风险。这就是我在生产代码中写的方式。

void free_list(struct node *head) 
{
    struct node *tmp;
    while(head) {
        tmp = head;
        head = head->next;
        free(tmp);
    }
}