在C语言中释放一个双链表
我有一个C语言的双链表,我不知道该如何释放它。我知道我必须遍历列表来释放每个节点。混淆之处在于,我的每个节点都有一个指向其他数据的指针,我不确定该如何释放这些数据 我的双链接列表如下所示:在C语言中释放一个双链表,c,memory,free,C,Memory,Free,我有一个C语言的双链表,我不知道该如何释放它。我知道我必须遍历列表来释放每个节点。混淆之处在于,我的每个节点都有一个指向其他数据的指针,我不确定该如何释放这些数据 我的双链接列表如下所示: typedef struct Node_ Node; typedef struct List_ List; struct Node_ { void *data; Node *next; Node *prev; }; struct List_ { Node *firstNod
typedef struct Node_ Node;
typedef struct List_ List;
struct Node_ {
void *data;
Node *next;
Node *prev;
};
struct List_ {
Node *firstNode;
Node *lastNode;
};
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);
}
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);
}
为了释放列表,我创建了一个名为list_free()的函数,该函数遍历列表,使用node_free()释放每个节点。这些函数如下所示:
typedef struct Node_ Node;
typedef struct List_ List;
struct Node_ {
void *data;
Node *next;
Node *prev;
};
struct List_ {
Node *firstNode;
Node *lastNode;
};
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);
}
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);
}
这里要解决的问题是node->data是指向另一个本身包含指针的结构的指针。在我的例子中,我使用相同的列表代码来存储两个不同的结构
在我看来,我有以下选择:
我的思路是正确的还是遗漏了一些明显的东西?这是我在C的第一次尝试,所以如果这完全是错误的,我不会感到惊讶。
你没有错,这是OOP解决的问题之一,特别是C++。
您可以在结构中添加指向函数成员的指针,该指针将用于释放其数据。基本上添加了一个C++析构函数。这将保持泛型而不会有太多重复。您的问题不在问题所指出的双链接列表中,而是在复合动态结构中。这就是手动内存管理的工作原理:您必须跟踪分配的内存。最好每次都坚持使用相同结构的节点->数据,这样预定义的例程就可以释放它。或者,在结构上使用标记,在运行时打开标记,在不同结构的分配程序之间进行选择(或者只使用函数指针来模拟C++析构函数)。< /P> < P> HMMM:创建<代码> DATAULFEX/Ung>函数,并调用它以获得数据指针。
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);
}
<>编辑:更改<代码>结构NoDEi定义,使其更类似于C++类:-< /P>
一种解决方案是提供一个函数指针,负责正确释放节点
typedef void(*NodeDataFreeFn)(void*);
列表\u free修改如下:
typedef struct Node_ Node;
typedef struct List_ List;
struct Node_ {
void *data;
Node *next;
Node *prev;
};
struct List_ {
Node *firstNode;
Node *lastNode;
};
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);
}
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);
}
免费数据示例:
void data_free_fn(void* data_ptr) {
// Add your custom stuff here.
free(data_ptr);
}
调用List_free的示例:
List_free(my_list, data_free_fn);
如果不想通过参数传递无数据函数指针,可以将其存储到列表结构中,如下所示:
struct List_ {
Node *firstNode;
Node *lastNode;
NodeDataFreeFn data_free;
};
免责声明:我没有测试此代码,它可能有缺陷…如果应用程序可以在您的列表中存储任意数据,则应用程序也有责任安排适当的清理。
这可以通过两种方式实现:
List\u free
之前,应用程序必须清除列表中的所有元素列表\u free
(和节点\u free
)中,该参数是指向删除器函数的指针:
typedef void (deleter_t*)(void*);
void Node_free(Node* node, deleter_t deleter)
{
if (deleter) (*deleter)(node->data);
free(node);
}
typedef void(deleter_t*)(void*)
void Node_free(Node*Node,deleter_t deleter)
{
if(deleter)(*deleter)(节点->数据);
自由(节点);
}
对于不必显式释放的数据,应用程序可以传递NULL deleter;对于可以通过单个free
调用释放的数据,该函数可以作为deleter传递。对于更复杂的数据,应用程序必须传递一个执行正确操作的函数您可能希望尝试的一种方法是实现一个内存池;在您不打算单独释放节点的情况下,实现这一点可能特别简单 只需
malloc
(或者mmap
,可能是/dev/zero
)一个大的块,然后您就可以通过简单的指针添加为一个节点“分配”内存。然后,取消分配整个列表就是free
ing(或munmap
ing)大块的问题
如果做得正确,这也可能会稍微提高性能。正如其他人所说,您已经发现在C中手动执行某些操作是一件很痛苦的事情,但必须执行。您可以使用函数指针使释放机制通用于您的数据,但您仍然必须实现该机制,以向下遍历每个节点中的数据层次结构,从而释放所有数据 这是一个很好的练习,值得为学习体验而做
但是还有一个非常有用的库,可以为您处理所有这些问题:。它为您跟踪层次结构,因此释放顶层会自动释放底层的所有内容。这是有据可查的。它是为samba开发的,现在仍在使用,因此维护得非常好。问题不是列表中有
prev
指针。问题在于,每种不同类型的节点都有不同的删除其内容的方法
因此需要有一个通用的节点_free
例程(您已经有了),它需要根据节点的类型进行调度。因此,要么节点需要包含泛型类型代码,要么它需要包含指向为该节点类型定制的析构函数例程的指针
无论哪种方式,你基本上都在做C++所做的事情。< / P > Hmm.。我会使用C++并依赖析构函数。祝你在C++中编写你的操作系统或内核驱动程序或嵌入式应用程序;Alexander Rafferty:如果你打算改变语言来避免这个问题,那么就换一种带有垃圾收集器的语言,例如Java。为什么不改成Lisp呢?它对列表有很好的支持。如果数据是空的,我可以使用列表中的任何类型的数据。这是我的问题。我对不同的数据类型使用相同的列表代码。如果我为我打算存储的每个数据类型创建单独的列表,我可以按照你的建议去做。你肯定有办法确定你的void指针的数据类型,对吗?只需在免费数据中使用它,但它不再是可重用的代码。每次添加新的数据类型时,都必须更改数据类型。我能想到的唯一其他方法是更改struct节点