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
相关产品推荐
相关产品推荐

