【问题标题】:Linked list containing other linked lists & free链表包含其他链表 & 免费
【发布时间】:2011-06-24 13:23:00
【问题描述】:

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

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

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

【问题讨论】:

  • 我会把释放数据的部分留给用户,所以他/她应该知道如何free每个元素指向的数据。链表的工作只是链接所有这些数据指针。

标签: c pointers data-structures linked-list void-pointers


【解决方案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】:

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

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

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

    【讨论】:

    • 我的列表确实有所有权,所有其他 void* 以及它们如何被释放由调用者处理,但我需要释放我创建的较小列表。我只想释放节点并列出结构而不是它们包含的调用者作业。这是在具有 1kb 内存的微处理器上运行,所以我想用最少的内存来解决这个问题。
    • @Hamza Yerlikaya- 如果是这种情况,我建议不要使用幻数,而是在每个链表单元格中设置一个位来指示数据是另一个列表还是客户端数据。然后,这将允许您在清理期间检查要使用的释放过程。我建议在幻数上这样做,因为幻数总是存在风险,即有人会存储也以幻数开头的数据,这会欺骗您的程序将非列表作为列表解除分配。存储一个额外的位永远不会导致这种歧义,并且至少与空间效率一样高。
    • 如何在结构中定义单个位?
    • @Hamza Yerlikaya- 您可能想查看位字段 (en.wikipedia.org/wiki/Bit_field) 以了解如何执行此操作。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-04
        • 1970-01-01
        • 2019-07-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多