You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言单链表节点删除代码疑惑:末尾两行逻辑不解

链表节点删除函数逻辑疑问解析

问题代码

void *delete_from_list(struct node** list, int n)
{
    struct node *entry = *list;
    while (entry) {
        if (entry->value == n) {
            *list = entry->next;
            free(entry);
        }
        list = &entry->next;
        entry = entry->next;
    }
}

疑问点

我能理解到free(entry);这一行的逻辑,但无法理解最后两行。我认为list = &entry->next是将entry->next的地址赋值给双指针list,下一行让entry指向后续节点。但如果free(entry)释放了entry指向的内存块,entry将不再指向有效内存,那么entry->next也会失效,这样entry = entry->next;这行逻辑就说不通了。


逻辑拆解

咱们一步一步捋清楚:

1. 未触发删除的常规遍历

这时候跳过if判断块,直接执行最后两行:

  • list = &entry->next:让双指针list指向当前节点entry内部next成员的内存地址。这么做是为了下一轮循环时,*list能直接操作当前节点的下一个节点的前驱指针(也就是当前节点的next)。
  • entry = entry->next:把entry移动到下一个节点,完成一次链表遍历的步进,逻辑完全通顺。

2. 触发删除的情况(找到目标节点)

这里就是你疑惑的核心,原代码其实存在不规范的地方,但我们先拆解它的逻辑:

  • 首先*list = entry->next:这行是把当前节点的下一个节点“接”到链表的前一段上——如果删的是头节点,*list就是原链表头,修改后链表头直接变成下一个节点;如果删的是中间节点,list指向的是前一个节点的next成员,修改*list就等于让前一个节点跳过当前节点,完成链表的断链操作。
  • 然后free(entry)释放当前节点的内存。这里你的担心完全正确:已经被释放的内存属于无效内存,标准C不允许再访问其中的内容。原代码之所以能“正常运行”,只是依赖了很多操作系统的一个非标准行为——刚释放的内存内容不会立刻被覆盖,所以后续还能读出entry->next的正确值。
  • 最后两行list = &entry->next和entry = entry->next:这里直接读取了已释放内存里的next值,属于未定义行为,虽然实际运行大概率没问题,但这是不规范、有风险的写法。

3. 更安全的改进写法

核心思路就是在释放节点前,先把entry->next的值保存下来,彻底避免访问已释放的内存:

void delete_from_list(struct node** list, int n)
{
    struct node *entry = *list;
    while (entry) {
        if (entry->value == n) {
            struct node *next_node = entry->next; // 提前保存下一个节点
            *list = next_node;
            free(entry);
            entry = next_node; // 直接用保存好的节点,不碰已释放内存
            list = &entry->next;
            continue; // 跳过后续两行,直接进入下一轮循环
        }
        list = &entry->next;
        entry = entry->next;
    }
}

内容的提问来源于stack exchange,提问作者Fary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 10:05:33