链表污染或节点劫持

发布时间:2026/7/24 3:09:03
链表污染或节点劫持 一、场景模拟1. 当前我有两个结构体一个用来存储节点另一个用来存储符合条件内容的节点。typedef struct TempNode { book_n *book_ptr; // 指向原始图书节点的指针 struct TempNode *next; } TempNode; // 临时链表头 typedef struct { TempNode *head; int count; } TempList; // 函数 TempList* filter_by_one_condition(TempList *input_list, SearchCondition *search); // 参数1TempList *input_list; // 输入链表要遍历的链表 // 参数2SearchCondition *search; // 查找条件 // TempList *new_list; // 返回的符合条件的新链表2. 假设输入的链表 input_list 中有三本书分别是 A、B、C其中符合条件的书是 A 和 C我们需要把 A 和 C 放到 new_list 节点中。二、代码逻辑1. 遍历 input_list 节点找到符合的节点 A、C。2. 创建 new_list 节点将节点 A 和 C 分别插入到 new_list 节点中。3. 返回新创建的 new_list 节点。// 错误写法 // curr_temp 是遍历节点 if (is_match) { // 符合条件 if (new_list-head NULL) { new_list-head curr_temp; // 直接把输入链表的节点 A 拿过来当头 tail curr_temp; } else { tail-next curr_temp; // 【致命】修改了 A 的 next 指针让它指向 C tail curr_temp; } }三、步骤模拟第一步处理节点 A匹配1.curr_temp指向A。2.new_list为空。3.new_list-head A。4.tail A。此时内存状态输入链表视角A - B - C还没变因为没动A-next结果链表视角Head - A第二步处理节点 B不匹配1.curr_temp指向B。2.is_match为假跳过不做任何操作。3. 循环继续curr_temp变为C。输入链表A - B - C结果链表Head - ATail 还是 A第三步处理节点 C匹配—— 灾难发生时刻1.curr_temp指向C。2.is_match为真。3. 此时tail是A。此时会把 A 的 next 指针强行改成指向 C如果没有其他指针指向 BB 就变成了孤儿节点内存泄漏。如果你后续还要遍历输入链表比如curr_temp curr_temp-next你会直接从 A 跳到 C永远漏掉 B 之后的所有节点如果 B 后面还有 D、E... 它们也全丢了。此时的内存状态[ Node A ] ──next──→ [ Node C ] ---------- ---------- | data: A | | data: C | | next: |──┐ | next: |──→ NULL (假设 C 原来是尾节点) ---------- │ ---------- └──────→ (原本指向 B现在被强制改向 C) [ Node B ] ── ️ 孤儿节点没人指向它了内存泄漏 ---------- | data: B | | next: |──→ ... ----------第四步潜在的崩溃如果 C 不是最后一个假设输入链表是A - B - C - D。C 匹配D 不匹配。1、执行tail-next C即A-next C。2、tail更新为C。3、循环结束。结果链表Head - A - C。但是在输入链表中C-next指向D。因为你没有创建新节点也没有把C-next设为NULL结果链表的尾部依然挂着 D当你遍历结果链表打印时打印 A。顺着A-next找到 C。打印 C。顺着C-next找到D。打印 D哪怕 D 根本不匹配条件结果污染结果链表里混入了不匹配的节点因为它们的next指针还连着原链表的后续部分。正确做法/* 输入链表完好无损[A] - [B] - [C] - [D] 结果链表独立新建[New_A] - [New_C] | | v v 指向A 指向C New_A-next 指向 New_C New_C-next 是 NULL 原链表 A-next 依然指向 B不受影响。 */ // 代码部分 while (curr_temp ! NULL) { // ... 判断 is_match ... if (is_match) { // 【关键步骤】为当前匹配的书申请一块新的内存空间 TempNode *new_node (TempNode *)malloc(sizeof(TempNode)); // 填充这个新节点 new_node-gt;book_ptr curr_temp-gt;book_ptr; // 指向真正的书 new_node-gt;next NULL; // 初始化 next // 链接到结果链表 if (new_list-gt;head NULL) { new_list-gt;head new_node; tail new_node; } else { tail-gt;next new_node; tail new_node; } new_list-gt;count; } curr_temp curr_temp-gt;next; // 继续检查下一本输入链表中的书 }四、总结1、破坏性修改直接复用节点意味着你要修改它的next指针来构建新链表。这会切断它在原链表中的连接导致原链表遍历中断或数据丢失。2、尾部污染除非你手动把最后一个匹配节点的next设为NULL否则结果链表会一直延伸到原链表的末尾包含大量不匹配的数据。3、为了避免链表污染新链表一定要malloc节点。