内核链表用法
以一个贯穿始终的 student
结构体和名为 student_list_head
的链表头为例
1 |
|
初始化
INIT_LIST_HEAD - 动态初始化链表头
对一个已分配内存、但未初始化的链表头进行初始化。这是动态创建链表时的必用函数
1 | // 假设链表头是另一个动态结构体的一部分 |
高级工具
list_entry - 从“连接扣”反查“本人”
内核链表的核心魔法。从指向list_head
成员的指针,反向计算出其宿主结构体的起始地址。list_for_each_entry
等宏在内部调用它
1 | // 假设只拿到了一个 list_head 成员的指针 list_ptr |
增
list_add_tail - 在链表尾部添加 (最常用)
将新节点添加到链表头(head
)的前面,实现尾部插入效果
1 | struct student *new_stu = malloc(sizeof(struct student)); |
list_add - 在链表头部添加
将新节点添加到链表头(head
)的后面,实现头部插入效果
1 | struct student *new_stu = malloc(sizeof(struct student)); |
删
list_del - 从链中断开节点
将指定节点从链表中“摘出”。节点内存并未释放,可以重新插入或手动释放
1 | // 假设 stu_to_delete 是要删除的节点指针 |
list_for_each_entry_safe - 安全遍历 (删除时专用)
遍历链表,同时允许在循环体内安全地删除当前节点
1 | struct student *p, *next_p; |
移动
一步完成“摘出”和“插入”
list_move - 移动节点到指定位置之后
将节点移动到目标节点的后面
1 | // 把 rose_node 移动到 jack_node 的后面 |
list_move_tail - 移动节点到指定位置之前
将节点移动到目标节点的前面。常用于将节点移动到队尾
1 | // 把 bill_node 移动到整个链表的末尾 (即链表头的前面) |
查 & 遍历
list_for_each_entry - 遍历所有节点 (最常用)
安全、方便地遍历链表,在循环中直接获得指向大结构体的指针
1 | struct student *p; |
list_for_each - 遍历链表节点指针
遍历链表的所有 list_head
节点(不直接获取宿主结构体指针),适合只操作链表连接结构时使用。
1 | struct list_head *pos; |
list_for_each
是基于指针操作的低层次遍历宏,不做类型安全转换
list_empty - 检查链表是否为空
判断链表是否只包含头结点。返回 true
(1) 或 false
(0)
1 | if (list_empty(&student_list_head)) |