在C中释放双向链表

时间:2010-11-03 10:31:27

标签: c memory free

我在C中有一个双重链接列表,我对如何释放它感到困惑。我知道我必须遍历列表释放每个节点。困惑在于我的每个节点都有一个指向其他数据的指针,我不确定如何释放它。

我的双重链接列表如下所示:

typedef struct Node_ Node;
typedef struct List_ List;

struct Node_ {
    void *data;
    Node *next;
    Node *prev;
};

struct List_ {
    Node *firstNode;
    Node *lastNode;
};

为了释放列表,我创建了一个名为List_free()的函数,该函数遍历列表,使用Node_free()释放每个节点。这些函数如下所示:

void *List_free(List *list)
{
    Node *next = list->firstNode;

    while(next)
    {
        Node *node = next;
        next = node->next;
        Node_free(node);
    }

    free(list);
}

void Node_free(Node *node)
{
    free(node->data);
    free(node);
}

这将导致其失败的地方是node-> data是指向另一个结构的指针,该结构本身包含指针。在我的例子中,我使用相同的列表代码来存储两个不同的结构。

我看到它的方式我有以下选择:

  1. 创建节点包含特定数据的列表。不太可重复使用。
  2. 找到另一种方法来跟踪节点数据中的指针。
  3. 我是在思考正确的方向还是错过了一些明显的东西?这是我在C的第一次尝试,所以如果这完全错误的话,我不会感到惊讶。

8 个答案:

答案 0 :(得分:5)

一种解决方案是提供一个负责正确释放节点的函数指针。

typedef void(*NodeDataFreeFn)(void*);

List_free被修改如下:

void *List_free(List *list, NodeDataFreeFn data_free)
{
    Node *next = list->firstNode;

    while(next)
    {
        Node *node = next;
        next = node->next;
        (*data_free)(node->data);
        free(node);
    }
    free(list);
}

示例data_free:

void data_free_fn(void* data_ptr) {
    // Add your custom stuff here.
    free(data_ptr);
}

对List_free的示例调用:

List_free(my_list, data_free_fn);

如果你不想通过参数传递数据自由函数指针,你可以将它存储到List结构中:

struct List_ {
   Node *firstNode;
   Node *lastNode;
   NodeDataFreeFn data_free;
};

免责声明:我没有测试此代码,可能是错误的......

答案 1 :(得分:2)

你没有错,这是OOP特别是C ++解决的问题之一。

您可以在结构中添加一个指向函数成员的指针,用于释放其数据。基本上手工添加C ++析构函数。这将保持通用性而不会有太多重复。

答案 2 :(得分:2)

如果应用程序可以在您的列表中存储任意数据,那么应用程序也有责任安排正确的清理。
这可以通过两种方式完成:

  1. 在调用List_free之前,应用程序必须清除列表中的所有元素。
  2. 您向List_free(和Node_free)添加了另一个参数,这是一个指向删除函数的指针:

    
    typedef void (deleter_t*)(void*);

    void Node_free(Node* node, deleter_t deleter) { if (deleter) (*deleter)(node->data); free(node); }

    对于不必显式释放的数据,应用程序可以传递NULL删除器,对于可以通过单个free调用释放的数据,该函数可以作为删除器传递。对于更复杂的数据,应用程序必须传递一个正确的函数。

答案 3 :(得分:0)

您的问题不在问题所示的双向链表中,而是在复合动态结构中。这就是手动内存管理的工作方式:您必须跟踪已分配的内容。最好每次尝试坚持节点 - >数据是相同的结构,以便预定义的例程可以释放它。或者,在结构上使用标记并在运行时打开标记,以在不同结构的释放例程之间进行选择(或者只使用函数指针来模拟C ++析构函数)。

答案 4 :(得分:0)

嗯:创建一个Data_free函数并为数据指针调用它。

void Data_free(void *ptr)
{
    /* first identify what data type ptr really is */
    /* and then deallocate memory used by ptr */
}

void Node_free(Node *node)
{
    /*free(node->data);*/
    Data_free(node->data);
    free(node);
}

编辑:更改struct Node_定义,使其更类似于C ++类: - )

struct Node_ {
    void *data;
    void (*freedata)(void*);
    struct Node_ *next;
    struct Node_ *prev;
}

答案 5 :(得分:0)

您可能想要尝试的一种方法是实现内存池;如果您不打算单独释放节点,那么实施起来可能特别容易。

只需malloc(或mmap,或许/dev/zero)一个大块,然后你可以通过简单的指针添加为一个节点“分配”内存。取消分配整个列表就是free(或munmap大块的问题。

如果操作正确,这也可能会略微提升性能。

答案 6 :(得分:0)

正如其他人所说的那样,你已经确定了一些在C语言中难以做到的事情,但必须要做。您可以使用函数指针使释放机制成为您数据的通用机制,但是您仍然需要实现机制来关闭每个节点中的数据层次结构以释放所有内容。

这是一项很好的练习,值得为学习经验而努力。

但是,还有一个非常有用的库可以为您处理所有这些:talloc。它会为您跟踪层次结构,因此释放顶层会自动释放下面的所有内容。这是有据可查的。它是为桑巴开发的,至今仍在那里使用,所以维护得非常好。

答案 7 :(得分:0)

问题不在于列表有prev个指针。问题在于,每种不同类型的Node都有不同的删除内容的方式。

所以需要一个通用的Node_free例程(你有),它需要调度节点的类型。因此,节点需要包含泛型类型代码,或者它需要包含指向为该节点类型定制的析构函数例程的指针。

无论哪种方式,你基本上都在做C ++为你做的事情。