从零开始的敲代码生活--数据结构篇(内核链表)

发布时间:2026/10/24 7:53:58

从零开始的敲代码生活--数据结构篇(内核链表) 一、内核链表基础概念普通链表数据域里面包含指针。 内核链表不把数据放在链表结点内部链表结点只存两个指针prev/next嵌入到自定义结构体中称为“侵入式链表”。 本质是双向循环链表有一个哨兵头结点不存有效数据哨兵的prev指向链表尾next指向链表第一个有效结点。优点通用一套链表代码可以管理任意自定义数据结构体双向循环头尾操作O(1)内核广泛使用不需要管理计数clen通过container_of宏由链表指针反推得到外部结构体首地址。缺点理解门槛高必须掌握container_of没有自带结点计数如需数量需要自己遍历统计。文件说明klisth.h内核链表头文件模拟linux内核list_headklist.c简单封装对外接口main_klist.c测试main函数注意Linux内核源码的list_head是纯头文件宏/内联函数没有.c文件这里为贴合文章格式做简单封装。二、头文件 klisth.h#ifndef _KLIST_H #define _KLIST_H #include stdio.h #include stdlib.h //内核链表结点只有两个指针无数据 typedef struct list_head { struct list_head *prev; struct list_head *next; }list_head; //container_of宏根据成员地址反推整个结构体首地址 #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - (unsigned long)(((type *)0)-member))) //初始化哨兵头结点 #define LIST_HEAD_INIT(name) { (name), (name) } #define LIST_HEAD(name) list_head name LIST_HEAD_INIT(name) //自定义数据结构体把list_head嵌入自己的结构体 typedef struct { int id; char name[20]; list_head node; //内核链表结点嵌入在这里 }Student_t; //函数声明 extern void list_init(list_head *head); extern void list_add_head(list_head *head, list_head *new); extern void list_add_tail(list_head *head, list_head *new); extern void list_del(list_head *pos); extern int list_is_empty(list_head *head); //遍历宏 #define list_for_each(pos, head) \ for(pos (head)-next; pos ! (head); pos pos-next) //遍历并拿到外部结构体指针 #define list_for_each_entry(stu, pos, head, member) \ for(pos (head)-next, stu container_of(pos, Student_t, member);\ pos ! (head);\ pos pos-next, stu container_of(pos, Student_t, member)) #endif三、功能实现 klist.c模拟内核链表基础操作内核中原为static inline这里封装成函数方便阅读。1. list_init 初始化哨兵头结点功能哨兵结点自环prev、next指向自己。#include klisth.h void list_init(list_head *head) { head-next head; head-prev head; }2. list_add_head 头插哨兵之后插入void list_add_head(list_head *head, list_head *new) { new-next head-next; new-prev head; head-next-prev new; head-next new; }3. list_add_tail 尾插哨兵的prev前面插入void list_add_tail(list_head *head, list_head *new) { new-next head; new-prev head-prev; head-prev-next new; head-prev new; }4. list_del 删除指定结点只摘链不free内存重要list_del只是把结点从链表摘出去不会释放结点对应的内存free由用户自己完成。void list_del(list_head *pos) { pos-prev-next pos-next; pos-next-prev pos-prev; pos-next NULL; pos-prev NULL; }5. list_is_empty 判断链表是否为空int list_is_empty(list_head *head) { return head-next head; }四、测试main函数 main_klist.c#include klisth.h int main(void) { //1.定义哨兵头结点 LIST_HEAD(stu_head); //申请3个学生结构体 Student_t *s1 malloc(sizeof(Student_t)); Student_t *s2 malloc(sizeof(Student_t)); Student_t *s3 malloc(sizeof(Student_t)); if(s1NULL||s2NULL||s3NULL) { printf(malloc error\n); return -1; } s1-id 1001; snprintf(s1-name,sizeof(s1-name),zhangsan); s2-id 1002; snprintf(s2-name,sizeof(s2-name),lisi); s3-id 1003; snprintf(s3-name,sizeof(s3-name),wangwu); //尾插把结构体内部的node挂入链表 list_add_tail(stu_head, s1-node); list_add_tail(stu_head, s2-node); list_add_tail(stu_head, s3-node); printf(遍历学生链表:\n); list_head *pos; Student_t *pstu; list_for_each_entry(pstu, pos, stu_head, node) { printf(id:%d name:%s\n, pstu-id, pstu-name); } //删除s2结点 list_del(s2-node); free(s2); s2 NULL; printf(删除lisi之后:\n); list_for_each_entry(pstu, pos, stu_head, node) { printf(id:%d name:%s\n, pstu-id, pstu-name); } //全部释放 while(!list_is_empty(stu_head)) { pos stu_head.next; list_del(pos); pstu container_of(pos, Student_t, node); free(pstu); } return 0; }五、编译运行内存检测编译gcc main_klist.c klist.c -o klist_demo运行程序./klist_demovalgrind检测内存泄漏valgrind --leak-checkfull ./klist_demo注意list_del()只做摘链不会free内存必须手动free外层结构体container_of是内核链表灵魂由链表成员指针找回整个结构体哨兵结点LIST_HEAD本身不存业务数据内核链表没有维护clen计数求长度需要遍历计数。
延伸阅读

更多相关文章

2026/10/23 21:54:41

OpenAI Codex Harness 深度解析

从大模型竞争,到 Agent 基础设施竞争。过去几年,AI 竞争的核心一直围绕: 参数规模Benchmark 分数推理能力上下文长度 但是进入 Agent 时代以后,一个新的关键词正在出现:Harness(智能体运行框架)…

2026/10/23 2:46:39

Graphiti:面向 AI Agent 的时序知识图谱框架详解-王仕宇

从 Ontology、Knowledge Graph 到 GraphRAG,让 AI Agent 拥有真正的长期记忆能力。 项目地址: https://github.com/getzep/graphiti 一、为什么需要 Graphiti? 过去一年,RAG(Retrieval-Augmented Generation&#xf…

2026/10/24 7:53:43

基于PCA9422与PIC18F46K42的电源管理方案设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/24 7:53:43

教育专用大模型开放架构:从涌现能力到应用落地避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/24 7:53:43

MFC无边框窗体圆角与GDI+自绘边框实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/24 7:53:43

在线互传文件网址:轻量级点对点直链传输方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/24 7:53:43

罗衣与皮囊:数字时代认知节能的交互设计法则

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/24 7:48:43

区块链在场证明:数字游民远程工作可信打卡方案

这些年我一直在帮一些远程工作团队搭建协作工具,见过了太多数字游民被“你怎么证明你刚才真的在工作”这种问题卡住的情况。客户想看实时进度,平台要审核工时,签证申请需要居住地证明,可一个人带着笔记本满世界跑的时候&#xff0…

2026/10/23 22:56:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/24 0:02:40

MATLAB缩进快捷键与批量格式化实战指南

1. 被自动缩进折磨的那些年如果你写过MATLAB代码,大概率经历过这种场景:从某个文档、网页或者聊天记录里复制了一段代码,粘贴进编辑器之后,每一行前面都带着莫名其妙的空格,有的行首是四个空格,有的是一个T…

2026/10/24 0:02:40

用Poco实现Qt风格信号与槽的跨平台C++方案

简介:本资源是一份面向C中级开发者的技术实践工程,聚焦于在非Qt框架环境下模拟实现Qt核心通信机制——信号与槽,借助Poco网络库的事件系统完成解耦式对象通信。适用于需在跨平台C项目中引入松耦合回调机制、但受限于无法集成Qt依赖的开发场景…

2026/10/24 0:02:40

ESXi裸机虚拟化安装指南:从硬件兼容到虚拟机部署

1. 为什么还要折腾ESXi:从一台闲置机器说起手里攒了几台淘汰下来的迷你主机,配置不算差,i5加16G内存,扔了可惜,装个桌面系统又觉得浪费。这种场景下,ESXi就成了一个绕不开的选择。它是一套裸机虚拟化平台&a…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑