c语言内核链表
在Linux中拥有大量的内核源码,在数据存储的这块位置拥有内核链表(双向循环链表)
由linux内核提供的链表文件,里面包含了多组内联函数和宏定义函数以及功能性函数。
内核链表中定义了多个函数,我们只需要知道参数的类型,然后填入参数,即可完成增删改查操作。
内核链表兼容了通用性,将指针域剥离出来,只封装指针域来管理节点。
内核链表优缺点:
优点:逻辑完整,函数封装完整,只需要调用,指针域独立管理节点。
缺点:用户无法修改内核链表的写法。
实现内核链表的使用
#include <stdio.h>
#include <stdlib.h>
#include "list.h"typedef struct node
{int data; // 数据域// 小结构体 这里为什么最好不用指针是因为内核函数里面有一个参数是取名字,并且初始化的时候取得是名字的地址&(name)struct list_head xNode;
} DNode, *DNODE;// 初始化头结点
DNODE initHead()
{// 申请空间DNODE Dhead = malloc(sizeof(DNode));if (Dhead == NULL){printf("大结构体空间申请失败!\n");return NULL;}// 初始化指针域INIT_LIST_HEAD(&(Dhead->xNode));return Dhead;
}
// 初始化普通节点
DNODE initNode(int data)
{// 申请空间DNODE xNode = malloc(sizeof(DNode));if (xNode == NULL){printf("小结构体空间申请失败!\n");return NULL;}// 初始化数据域xNode->data = data;// 初始化指针域INIT_LIST_HEAD(&(xNode->xNode));return xNode;
}// 插入节点
void insertNode(DNODE Dhead, DNODE xnode)
{// 插入头节点之前// list_add(&(xnode->xNode), &(Dhead->xNode));// 插入头节点之后list_add_tail(&(xnode->xNode), &(Dhead->xNode));
}// 遍历链表
void showList(DNODE Dhead)
{// 两种写法遍历链表struct list_head *pos, *safe_pos; // 小结构体指针DNODE safe_node, node; // 大结构体指针printf("head->");// 正向遍历 list_for_each// list_for_each(pos, &(Dhead->xNode))// 逆向遍历 list_for_each_prev// list_for_each_prev(pos, &(Dhead->xNode))// 正向安全遍历// list_for_each_safe(pos, safe_pos, &(Dhead->xNode))// // 逆向安全遍历// // list_for_each_prev_safe(pos, safe_node, &(Dhead->xNode))// {// /*// * 通过list_entry方法放入小结构体地址获取到大结构体地址// * pos小结构体地址// * DNode大结构体类型// * xNode小结构体名字// */// DNODE newName = list_entry(pos, DNode, xNode);// printf("%d->", newName->data);// }/*** 而安全跟不安全的区别就是,不安全的不支持遍历删除,如果在循环内部删除了当前节点,可能会导致遍历指针 (pos) 指向一个已经被删除的节点,从而引发未定义行为。* 上下两种遍历方式区别* 跟上面正向的却别就是上面方法需要通过小结构体找到大结构体再通过大结构体访问数据* 而下面这种方法遍历访问数据直接就通过大结构体访问*/// 大结构体正向遍历// list_for_each_entry(safe_node, &(Dhead->xNode), xNode)// 正向安全list_for_each_entry_safe(safe_node, node, &(Dhead->xNode), xNode){/** 通过list_entry方法放入小结构体地址获取到大结构体地址* pos小结构体地址* DNode大结构体类型* safe_node 大结构体节点*/printf("%d->", safe_node->data);}printf("\n");
}// 删除节点
void del_node(DNODE Dhead, int data)
{// 赋初始值为头结点地址DNODE pos = Dhead;// 找到符合条件的大结构体节点list_for_each_entry(pos, &(Dhead->xNode), xNode){if (pos->data == data)break;}// if (pos != Dhead)// 如果链表非空if (!list_empty(&(pos->xNode))){list_del(&(pos->xNode));}
}// 查找节点
DNODE find_node(DNODE Dhead, DNODE newFindNode)
{DNODE pos = Dhead;// 找到符合条件的大结构体节点list_for_each_entry(pos, &(Dhead->xNode), xNode){if (pos->data == newFindNode->data)break;}// 如果找到就返回,否则返回头结点return pos != Dhead ? pos : NULL;
}// 移动节点newNode移动节点,newNode2目标节点
void move_node(DNODE head, DNODE newNode1, DNODE newNode2)
{DNODE pos = find_node(head, newNode1);if (pos == head){return;}DNODE move_to_pos = find_node(head, newNode2);if (move_to_pos == head){return;}/*** 参数1要移动的节点,参数2目标节点* 这里移动是移动到目标节点的头结点之前*/// 移到某某节点之前// list_move(&(pos->xNode), &(move_to_pos->xNode));// 移到某某节点之后list_move_tail(&(pos->xNode), &(move_to_pos->xNode));
}// 修改节点
void updata_node(DNODE Dhead, DNODE newNode, DNODE newNode2)
{DNODE pos = find_node(Dhead, newNode);if (pos == Dhead){return;}pos->data = newNode2->data;
}int main()
{// 初始化头结点DNODE Dhead = initHead();// 生成5个节点插入进去for (int i = 0; i < 4; i++){// 初始化数据域DNODE xnode = initNode(i);insertNode(Dhead, xnode);}DNODE Dhead1 = initHead();for (int i = 6; i < 12; i++){// 初始化数据域DNODE xnode = initNode(i);insertNode(Dhead1, xnode);}del_node(Dhead, 1);showList(Dhead);// 原链表不清除list_splice(&(Dhead1->xNode), &(Dhead->xNode));// 这里指针乱了,所以将指针清空,最好把它释放掉Dhead1 = NULL;free(Dhead1);// 原链表清除// list_splice_init(&(Dhead1->xNode), &(Dhead->xNode));showList(Dhead);// 创建要查找的节点DNODE newFindNode = initNode(11);DNODE newNode = find_node(Dhead, newFindNode);// printf("%d\n", newNode->data);// 移动节点DNODE moveNode1 = initNode(7);DNODE moveNode2 = initNode(2);move_node(Dhead, moveNode1, moveNode2);showList(Dhead);// 修改节点DNODE upDateNode = initNode(2);DNODE upDateNode2 = initNode(200);updata_node(Dhead, upDateNode, upDateNode2);showList(Dhead);return 0;
}
内核链表一些方法源码
#ifndef __DLIST_H
#define __DLIST_H#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ({ \const typeof( ((type *)0)->member ) *__mptr = (ptr); \(type *)( (char *)__mptr - offsetof(type,member) ); })#define LIST_POISON1 ((void *)0x00100100)
#define LIST_POISON2 ((void *)0x00200)// 创建指针域结构体
struct list_head
{struct list_head *next, *prev;
};#define LIST_HEAD_INIT(name) {&(name), &(name)}#define LIST_HEAD(name) \struct list_head name = LIST_HEAD_INIT(name)/** ptr 小结构体的指针* 初始化的时候让他们的前驱后链指向自己*/
#define INIT_LIST_HEAD(ptr) \do \{ \(ptr)->next = (ptr); \(ptr)->prev = (ptr); \} while (0)/*** 插入一个节点到链表中需要重新指向的前驱后链交换 静态方法*/
static inline void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next)
{next->prev = new;new->next = next;new->prev = prev;prev->next = new;
}/*** 传入两参数* new 当前结构体的小结构体* head头结点的小结构体*/
static inline void list_add(struct list_head *new, struct list_head *head)
{__list_add(new, head, head->next);
}/***倒链*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{__list_add(new, head->prev, head);
}// 删除方法
static inline void __list_del(struct list_head *prev, struct list_head *next)
{next->prev = prev;prev->next = next;
}/*** 删除节点 静态方法* 要删除数据的小结构体*/
static inline void list_del(struct list_head *entry)
{__list_del(entry->prev, entry->next);entry->next = (void *)0;entry->prev = (void *)0;
}/*** 删除节点,又把删除的节点重新创建,只是没有链接*/
static inline void list_del_init(struct list_head *entry)
{__list_del(entry->prev, entry->next);INIT_LIST_HEAD(entry);
}/*** 这里移动是移动到目标节点的头结点之后*/
static inline void list_move(struct list_head *list,struct list_head *head)
{__list_del(list->prev, list->next);list_add(list, head);
}/*** 这里移动是移动到目标节点的头结点之后*/
static inline void list_move_tail(struct list_head *list,struct list_head *head)
{__list_del(list->prev, list->next);list_add_tail(list, head);
}/*** 判断链表是否为空*/
static inline int list_empty(struct list_head *head)
{return head->next == head;
}static inline void __list_splice(struct list_head *list,struct list_head *head)
{struct list_head *first = list->next;struct list_head *last = list->prev;struct list_head *at = head->next;first->prev = head;head->next = first;last->next = at;at->prev = last;
}/*** 合并链表,不清空原链表*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{if (!list_empty(list))__list_splice(list, head);
}/*** 合并链表,并清空被合并的链表*/
static inline void list_splice_init(struct list_head *list,struct list_head *head)
{if (!list_empty(list)){__list_splice(list, head);INIT_LIST_HEAD(list);}
}/*** 通过小结构体地址获取大结构体地址*/
#define list_entry(ptr, type, member) \((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))/*** 正向插入*/#define list_for_each(pos, head) \for (pos = (head)->next; pos != (head); \pos = pos->next)
/*** 逆向插入*/
#define list_for_each_prev(pos, head) \for (pos = (head)->prev; pos != (head); \pos = pos->prev)/****/
#define list_for_each_safe(pos, n, head) \for (pos = (head)->next, n = pos->next; pos != (head); \pos = n, n = pos->next)/********往前遍历(安全模式)************/
#define list_for_each_prev_safe(pos, n, head) \for (pos = (head)->prev, n = pos->prev; pos != (head); \pos = n, n = pos->prev)/*** 大结构体遍历*/
#define list_for_each_entry(pos, head, member) \for (pos = list_entry((head)->next, typeof(*pos), member); \&pos->member != (head); \pos = list_entry(pos->member.next, typeof(*pos), member))/*** 大结构体安全遍历*/
#define list_for_each_entry_safe(pos, n, head, member) \for (pos = list_entry((head)->next, typeof(*pos), member), \n = list_entry(pos->member.next, typeof(*pos), member); \&pos->member != (head); \pos = n, n = list_entry(n->member.next, typeof(*n), member))#endif
总结方法
list_head
指针域结构体名 内核固定写死的不能修改LIST_HEAD_INIT
:初始化链表头 struct list_head my_list = LIST_HEAD_INIT(my_list); 只不过这里没有给前驱后链后面自己给就行INIT_LIST_HEAD(小结构体名)
创建单个独立的结构体指针 ,创建出来的都是单个的至于哪个做头结点自己标记下,后面节点需要给上数据域
– 1.如果要创建头结点INIT_LIST_HEAD(&(Dhead->xNode));
开辟空间后的头结点的指针域
– 2.如果要创建后面节点INIT_LIST_HEAD(&(xNode->xNode));
开辟空间后每个结构的指针域,还要给上数据域- 插入数据 (参数1:上面标记好的头结点作为链表头地址 ,, 参数2:要插入的节点)
–list_add
头插 (参数1:插入的节点的指针域,, 参数2:头结点的指针域)list_add(&(xnode->xNode), &(Dhead->xNode)); 取地址
–list_add_tail
尾插参照头插- 遍历数据
–list_for_each(pos, &(Dhead->xNode))
正向遍历
–list_for_each_prev(pos, &(Dhead->xNode))
逆向遍历
–list_for_each_safe(pos, safe_pos, &(Dhead->xNode))
正向安全遍历
–list_for_each_prev_safe(pos, safe_node, &(Dhead->xNode))
逆向安全遍历
–list_for_each_entry(safe_node, &(Dhead->xNode), xNode)
大结构体正向遍历
–list_for_each_entry_safe(safe_node, node, &(Dhead->xNode), xNode)
大结构体正向安全遍历
安全定义:安全跟不安全的区别就是,不安全的不支持遍历删除,如果在循环内部删除了当前节点,可能会导致遍历指针 (pos) 指向一个已经被删除的节点,从而引发未定义行为。- 删除数据
list_del(&(pos->xNode));
list_del_init
删除节点并重新注册个- 合并列表
–list_splice(&(Dhead1->xNode), &(Dhead->xNode));
原链表不清除
–list_splice_init(&(Dhead1->xNode), &(Dhead->xNode));
原链表清除
判断链表是否为空list_empty(&(pos->xNode))
移动
–list_move(&(pos->xNode), &(move_to_pos->xNode));
参数1要移动的节点,参数2目标节点, 这里移动是移动到目标节点的头结点之前
–list_move_tail(&(pos->xNode), &(move_to_pos->xNode));
这里移动是移动到目标节点的头结点之后