Linux中List.h文件的分析和应用

本文深入解析Linux内核中的双向循环链表(List.h)操作,包括链表的初始化、插入、删除、替换、移动、状态检测、旋转、切分和合并等操作。此外,还介绍了通过宏定义进行链表遍历的各种方式,以及哈希表在链表中的应用。内容详实,适合对Linux内核和数据结构感兴趣的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        本文介绍Linux中List.h文件,并对其详细分析,此文件的双向链表短小精湛,值得借鉴。此文件中还有哈希表的应用。

目录

一、通过函数定义的方式对双向循环链表的操作    
1. List.h源码位置    
2. list_head结构    
3. 头结点的初始化    
4. 双向循环链表的插入操作    
(1)基本插入函数    
(2)首部插入函数    
(3)尾部插入函数    
(4)与非循环单链表的插入操作的区别    
5.双向循环链表的删除操作    
(1)删除    
(2)与非循环单链表删除操作的区别    
6.双向循环链表的替换操作    
(1)替换    
7.双向循环链表的移动操作    
8.双向循环链表的状态检测    
9.双向循环链表的旋转操作    
10.双向循环链表的切分操作    
11.双向循环链表的合并操作    
二、通过宏定义的方式对双向循环链表进行遍历    
1.基本的宏    
(1)list_entry宏    
(2)list_first_entry宏    
2.双向循环链表的遍历    
(1)从左向右遍历    
(2)从右向左遍历    
(3)安全的向前向后遍历    
(4)使用遍历    
(5)list_prepare_entry宏    
(6)下一个结点开始遍历    
(7)当前结点开始遍历    
(8)安全版本    
三、哈希链表的操作    

(注:基于Linux3.5版内核)

一、通过函数定义的方式对双向循环链表的操作

1. List.h源码位置

        List.h文件位于include/linux目录,内容为双向循环链表的操作,对双向循环链表的定义如下:(与单链表相比,双链表的每个结点同时具有前驱指针和后驱指针,操作会更方便)

(1)首节点,存放第一个有效数据的结点。

(2)尾结点,存放最后一个有效数据的结点,在双向循环链表中,头结点的前驱指针指向尾结点,尾结点的后驱指针指向头结点。

(3)头结点:

        1)头结点数据类型与首结点类型一样,首结点什么类型,头结点就是什么类型。

        2)头结点一般不存放有效数据。

        3)头结点是首结点前面的那个结点。

        4)为了方便操作链表而提出的。

(4)头指针,指向头结点的指针,即存放头结点地址。

2. list_head结构

        linux/types.h文件中定义了list_head结构,原型如下:

struct list_head 
{
		struct list_head *next; 
		struct list_head *prev;
};

        在实际应用中,此结构经常作为成员与其他数据类型一起组成一个新的结构体,比如类似如下结构:

struct userinfo{  
    char username[20];  
    char password[20];  
    struct list_head list;  
};

        在以上述结构为结点的双向循环链表中,userinfo结构的list成员就是用来链接链表的各个成员的。

3. 头结点的初始化

        在下面代码中,前面两个宏实现的功能与后面那个内联函数实现的功能一样。目的都是用来初始化双向循环链表的头指针,让其头指针指向的list_head型结构体变量的前/后驱指针成员指向头指针本身,即为空链表。

1.	#define LIST_HEAD_INIT(name) { &(name), &(name) }  
2.	#define LIST_HEAD(name)  struct list_head name = LIST_HEAD_INIT(name)  
3.	
4.	static inline void INIT_LIST_HEAD(struct list_head *list)  
5.	{  
6.	  list->next = list;  
7.	  list->prev = list;  
8.	}  

        比如在应用时,需使用上述任意一种方法初始化头指针,这里使用内联函数初始化头指针,如下代码:

struct userinfo  userlist;

INIT_LIST_HEAD(&(userlist.list)); 

         注意:函数参数传递是值传递(一个副本),上述参数传递list_head型结构体变量的地址。详见csdn博客上的文章。

4. 双向循环链表的插入操作(一个参数为待插入元素地址,一个为链表头结点地址)

         对于插入操作,总共定义了三个函数,后两个函数都会调用前一个函数,详见下面的代码。第一个为基础函数,第二个为在首部插入,适用于堆栈;第三个为在尾部插入结点,适用于队列。

/*
 * Insert a new entry between two known consecutive entries.
* This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
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;
}
/*
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
Static  inline  void  list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}
/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
* Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
Static  inline  void  list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

1)基本插入函数

         分析其参数,第一个参数new,是一个类型为list_head的指针,指向要增加的结点,而prev和next也都是list_head类型的指针,顾名思义,就是指向增加的位置的前面和后面的结点。

        在函数内部,首先是把要增加位置的后面的结点的prev元素赋值为new,再把new的next元素指向后面结点,意思是先把后面的结点与新增结点相连;然后是把new的prev元素指向前面结点,再把前面结点的next赋值为new,意思是把前面的结点与新增结点相连。如下图:

 2)首部插入函数 

        与尾部插入函数的区别是传入__list_add函数的第2、3个参数不一样,这样应用非常巧妙。

        根据上述代码,首部插入函数中调用__list_add 函数传入的参数分别为:new, head, head->next。(这三个参数可以另解为:新结点、头结点、首结点)

        这里的head一般指struct  list_head类型变量的地址,如第3中&(userlist.list),类似于非循环单链表的头指针

        首先,当链表为空链表时,head指针( 即上述例子中的&(userlist.list) )的前驱指针和后驱指针都指向了它自己。根据首部插入函数中调用__list_add 函数传入的参数可以很顺利地分析出在首部插入的过程,此时首结点即为new指向的结点。

        其次,当链表至少有一个除头结点外的结点时,也能很顺利地分析出首部插入过程,此时的首结点也为new指向的结点。

3)尾部插入函数

        与首部插入函数的区别是传入__list_add函数的第2、3个参数不一样,这样应用非常巧妙。

        根据上述代码,首部插入函数中调用__list_add 函数传入的参数分别为:new, head->prev, head。(新结点,尾结点,头结点)

        这里的head一般指struct  list_head类型变量的地址,如第3中&(userlist.list),类似与非循环单链表的头指针

        首先,当链表为空链表时,head指针( 即上述例子中的&(userlist.list) )的前驱指针和后驱指针都

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值