8

我有一个通用的链表实现,它的节点结构包含一个 void* 到数据和一个列表结构,它包含对 head 的引用。现在这是我的问题,链表中的节点可能通过其 void* 持有对另一个链表的引用。当我释放包含较小列表的较大列表时,这会导致内存泄漏。所以我想知道有没有办法检查 void* 是否指向另一个列表,所以我跟随并释放它或者只是数据。

如果我在我的结构的开头添加一个键,我可以通过取消引用 void* 来检查它并找出它是一个列表?

编辑:调用者不插入他们由我的函数插入的较小列表我不希望调用者只处理他们持有指针的列表。

4

4 回答 4

6

这个问题实际上取决于清理列表中的条目是谁的责任。如果你的结构负责清理void *字段引用的内存,那么你手头就有一个更大的问题,即给定一个void *任意内存块的引用,你永远不知道释放它的正确方法是什么。例如,如果您有一个沿 C++ 行的动态数组的实现std::vector,那么您void *可能指向一个本身包含指针的结构,并且您的列表需要知道它必须下降到该结构中才能递归释放它的动态分配块。你所描述的情况,你正在泄漏一个嵌套列表——只是这个更普遍问题的一个特例。

另一方面,如果列表不负责清理void *它所存储的 s 引用的内存,那么您根本不必担心这个问题。

如果您的列表确实具有所有权语义并且需要清理存储在其中的元素的内存,我强烈建议您不要使用幻数来确定您是否有嵌套列表。相反,您可能应该让客户端为您提供一个函数指针,其中包含一个释放例程,以在插入到列表中的元素上运行。这样,您的代码可以使用用户提供的清理代码来确保清理存储在列表中的任何元素。

于 2011-02-10T01:19:58.833 回答
1

这不仅仅是你void*可以指向一个列表。它可以指向任何动态分配的内存。

GLib 处理此问题的方式是说调用者有责任确保void *data释放列表中指向的任何内容。请参阅http://library.gnome.org/devel/glib/unstable/glib-Doubly-Linked-Lists.html#g-list-free

void *data另一种方法(GLib 也提供)是创建一个函数,该函数接受一个函数指针并在遍历列表时调用每个函数指针。抬头看g_list_free_full

于 2011-02-10T01:19:50.563 回答
1

我的建议是尽可能简化一些事情,并简单地确保一个链表只包含一种类型的对象。

如果你不能这样做,我可能会让列表中的每个节点不仅包含一些数据,而且还包含一个指向知道如何正确释放该类型项目的函数的指针。不可避免地,在您为链表编写特殊代码两周后,您会决定还需要另一个幻数来保存动态数组等。

于 2011-02-10T01:20:02.297 回答
0

回答“如果我在结构的开头添加一个键,我可以通过取消引用 void* 来检查它并找出它是一个列表”的智慧的问题是什么?

是的,你可以这样做,但很少有人会推荐它。只要真正确定“魔法”值不可能以其他方式出现。这是一个很大的问题。您想考虑您可能指向的其他内容以及当表示为无符号整数时可能采用的值。请记住,如果您决定它是一个列表,您将释放它,因此如果您错了可能会崩溃和烧毁。

最简单有效的解决方案是,如果您需要一个节点知道它指向一个列表,请在节点中提供一个标志来说明这一点。

如果你真的想让列表自己负责释放它的所有内容,你需要的不仅仅是一个标志,你需要知道如何做每一个释放。这可能是一个 id 或类似指向释放其内容的函数的指针。

于 2011-02-10T02:32:07.857 回答