我有一个通用的链表实现,其中包含一个包含void * to data的节点struct和一个包含对head的引用的list结构。现在这是我的问题,链表中的节点可以通过其void *保存对另一个链表的引用。当我释放包含较小列表的较大列表时,这会导致内存泄漏。所以我想知道有没有办法检查void *是否指向另一个列表,所以我关注并释放它也只是数据。
如果我在我的struct的开头添加一个关键字,我可以通过解除引用void *来检查它,并找出它是一个列表?
编辑:调用者不插入我的函数插入的较小列表我不希望调用者只处理他们持有指针的多个列表。
答案 0 :(得分:6)
这个问题实际上取决于清理列表中的条目的责任。如果你的结构负责清理void *
字段引用的内存,那么你手头有一个更大的问题,即给定void *
引用一些任意的内存块你永远不会知道解除分配的正确方法是什么。例如,如果你在C ++ std::vector
的行中有一个动态数组的实现,那么你的void *
可能指向一个本身包含指针的结构,你的列表需要知道它必须下降到该结构中以递归方式释放其动态分配的块。您正在描述的情况,即泄漏嵌套列表的情况 - 只是这个更普遍问题的一个特例。
另一方面,如果列表不负责清理它存储的void *
所引用的内存,那么你根本不应该担心这个问题。
如果您的列表确实具有所有权语义并且需要清理存储在其中的元素的内存,我强烈建议您不要使用幻数来确定您是否有嵌套列表。相反,您应该让客户端为您提供一个函数指针,其中包含一个释放例程,以便在插入到列表中的元素上运行。这样,您的代码可以使用用户提供的清理代码来确保清理列表中存储的所有元素。
答案 1 :(得分:1)
这不仅仅是您的void*
可以指向列表。它可以指向任何动态分配的内存。
GLib处理此问题的方式是说调用者有责任确保释放列表的void *data
指向的任何内容。请参阅http://library.gnome.org/devel/glib/unstable/glib-Doubly-Linked-Lists.html#g-list-free。
替代方法(GLib也提供)是创建一个函数,它接受一个函数指针,并在每个void *data
遍历列表时调用它。查看g_list_free_full
。
答案 2 :(得分:1)
我的建议是,如果可以简化一些事情,只需确保一个链表只包含一种类型的对象。
如果你不能这样做,我可能会让列表中的每个节点不仅包含一些数据,还包含一个指向知道如何正确释放该类型项的函数的指针。不可避免地,在为链表创建特殊代码两周后,您将决定还需要另一个幻数来保存动态数组等。
答案 3 :(得分:0)
回答“如果我在我的结构的开头添加一个关键字,我可以通过取消引用void *来检查并找出它是一个列表?”
的智慧是什么的问题。是的,你可以这样做,但很少有人会推荐它。只是确实确保不会发生'魔术'值。这是一个非常大的问题。您想要考虑您可能指向的其他内容以及在表示为无符号整数时可能需要的值。请记住,如果您认为它是一个列表,那么您将释放它,因此如果您错了,可能会崩溃并烧毁。
最简单有效的解决方案是,如果您需要一个Node知道它指向一个列表,请在节点中提供一个标记,说明这一点。
如果你真的希望列表自己负责释放所有内容,你需要的不仅仅是一个标志,你需要知道每个免费的方法。这可能是一个id或类似指向函数的指针,它释放了它的内容。