【问题标题】:Is free() missing in this "good taste" and "bad taste"code?这个“好品味”和“坏品味”代码中是否缺少 free() ?
【发布时间】:2019-05-22 09:19:19
【问题描述】:

在一次关于 Linus Torvalds 的采访中,他分享了拥有“好品味”的重要性。他用以下代码解释了良好的品味。

带有“坏味道”的代码

remove_list_entry(entry)
{
    prev = NULL;
    walk = head;

    // Walk the list

    while (walk != entry) {
        prev = walk;
        walk = walk->next;
    }

    // Remove the entry by updating the head
    // or the previous entry

    if (!entry)
        head = entry->next;
    else
        prev-next = entry->next;
}

带有“好品味”的代码

remove_list_entry(entry)
{
    // The "indirect" pointer points to the
    // *address* of the thing we'll update

    indirect = &head;

    // Walk the list, looking for the thing that
    // points to the thing we want to remove

    while ((*indirect) != entry)
        indirect = &(*indirect)->next;

    // .. and just remove it
    *indirect = entry->next;
}

两个例子都没有使用free()来释放要删除的节点的内存。有人能告诉我为什么代码是这样写的吗?还是我对 C 或链表的概念有误?

【问题讨论】:

  • 你能提供采访的链接/参考吗?
  • 这取决于列表的总体设计。从列表中删除一个节点并不意味着该节点可能不再被使用。
  • 抛开风格不谈,上面的例子不是坏了吗?
  • 您可以在What is the pointer to pointer technique for the simpler traversal of linked lists? 找到一个关于指针到指针链表处理技术的问题,该问题作为An interesting C linked list idiom 的副本关闭。
  • @500-InternalServerError 两者都只是伪代码。我不希望它们中的任何一个是完整的。在真正的“好品味”示例中,head 可能不是全局变量。一些NULL 检查也将包括在内。

标签: c linked-list


【解决方案1】:

注意这个函数的语义也很重要。它旨在从列表中删除一个节点,而不是像您在问题中建议的那样删除该节点。至少函数名称暗示了这一点。如果它在不宣传该语义的情况下删除节点,我会感到惊讶。

此外,我们无法单独知道对象是如何分配的,因此该函数无法合理地假设它是一个堆对象和free() 它 - 如果对象是静态的,这将导致运行时错误。

调用者可能正在从列表中删除对象并继续使用它,此函数的责任是维护列表而不是列表中具有独立存在的对象。 entry 的所有者有责任管理其内存。

【讨论】:

  • 和 free() 它 - 如果对象是静态的,这将导致运行时错误:释放尚未由 malloc 和朋友分配的东西不一定会触发运行时错误,它只是 UB。
  • @Jabberwocky 这将是构建时未捕获的语义错误。从这个意义上说,在运行时发生错误。这就是我的意思,不一定是运行时环境明确捕获的错误。我希望这很清楚?
  • 对我来说(并且一直是)很清楚,但对 OP 来说可能不清楚。
  • @Jabberwocky 谢谢,您的评论很清楚。
【解决方案2】:

在这种情况下,极简主义体现了良好的品味;第二个例子比第一个例子不那么华丽,但更重要。坏品味代码有一些错误,但关键是额外的变量和 if 语句是多余的。

品味是一种主观品质,有很多理由支持这两种方法。对我来说,品味一直与可读性有关。可读性并不明显有利于这两种方法——TLDR 与过于复杂;没有打扰 (TC;DB)。

编译器技术对时尚专家的品味产生了影响。很久以前,当窥视孔优化是事情时,第二个版本是首选;它生成的代码要少得多。在不那么古老的日子里,编译器玩弄数据流分析,并喜欢第一个版本,因为它的指针追逐显然不是隐藏的别名(与第二个不同)。现代编译器,如此时髦的内存和 cpu 允许更好的别名检查,所以回到了第一个。即便如此,在您的示例中,它还是有 3 条指令不同。

【讨论】:

    猜你喜欢
    • 2016-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-10
    相关资源
    最近更新 更多