当前位置: 首页 > news >正文

链表污染或节点劫持

一、场景模拟:

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); // 参数1:TempList *input_list; // 输入链表,要遍历的链表 // 参数2:SearchCondition *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 -> A(Tail 还是 A)

第三步:处理节点 C(匹配)—— 灾难发生时刻

1.curr_temp指向C

2.is_match为真。

3. 此时tailA

此时会把 A 的 next 指针,强行改成指向 C!

如果没有其他指针指向 B,B 就变成了"孤儿节点"(内存泄漏)。

如果你后续还要遍历输入链表(比如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!

当你遍历结果链表打印时:

  1. 打印 A。
  2. 顺着A->next找到 C。
  3. 打印 C。
  4. 顺着C->next找到D
  5. 打印 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节点。

http://www.jsqmd.com/news/1251817/

相关文章:

  • 可信数据空间技术架构:TC609国标下的六大能力模块拆解
  • 深入解析MSPM0 I2C模块:从协议原理到寄存器配置与调试实践
  • 内容创作者如何选择职业认证:通用型与技术专项对比
  • AI Agent记忆机制解析与优化实践
  • 全球半固态无人机电池市场发展规模分析及投资趋势预测报告2026年版
  • Context Engine:AI应用开发的工程化上下文管理方案
  • 联想小新Pro笔记本Type-C耳机无法识别的排查与解决
  • GEN-1通用AI模型:跨领域任务处理与核心技术解析
  • 解决Windows系统msimg32.dll缺失问题的完整指南
  • YOLO小目标检测优化策略与实践指南
  • 大语言模型逻辑推理稳定性诊断:基于学习软前缀的压力测试方法
  • LLM微调实战:LoRA技术在金融问答系统中的应用
  • SN65DSI86/96硬件设计实战:MIPI DSI转eDP桥接芯片PCB布局与信号完整性指南
  • YOLO系列算法在无人机目标检测中的优化与应用
  • 大模型时代事件抽取技术的核心价值与实践
  • 国内卡地亚官网售后维修保养服务指南权威公示(2026年7月最新) - 卡地亚服务中心
  • MSPM0 I2C DMA触发机制详解:从FIFO事件到高效数据流
  • Windows本地AI开发环境搭建:Ollama与OpenClaw实战指南
  • AI论文写作工具对比:千笔与笔捷Ai助力本科生高效写作
  • MSPM0技术手册更新解析:FACTORYREGION与UNICOMM模块的嵌入式应用
  • LLM上下文剖析器开发指南:工具调用与智能体性能优化实践
  • 低代码破局政务协同:跨部门工作流打通实战落地
  • 深度强化学习在MOBA游戏AI开发中的实践指南
  • 他本来要被开掉,结果三个月后成了“陪诊一哥“
  • AI视频生成API成本优化技术与商业实践
  • IDEA 中的 Line Separator(换行符)
  • 操作系统存储器管理:原理、策略与性能优化实战
  • AI技术如何重塑日常生活与健康管理
  • 2026 年更新:文登知名的肉驴制造企业综合实力解析,揭秘它如何颠覆传统农业的秘密-坤达养殖 - 行业鉴选官
  • Linux智能缓冲调度与异步I/O性能优化实战