链表(循环双向链表)是Linux内核中最简单、最常用的一种数据结构。
No. | 主要 函数 | 说明 |
1. | list_add | 在 head 之后追加一个节点 |
2. | list_add_tail | 在 head 之前追加一个节点, 也就是在末尾追加一个节点 |
3. | list_del | 删除一个节点, 并将这个节点的next, prev 置为 NULL |
4. | list_del_init | 删除一个节点并初始化删除的节点 |
5. | list_replace | 替换一个节点 |
6. | list_replace_init | 替换一个节点, 并初始化被替换的节点 |
7. | list_move | 移动节点到 head 之后 |
8. | list_move_tail | 移动节点到 head 之前 |
9. | list_is_last | 判断节点是否是链表中最后一个 |
10. | list_empty | 判断链表是否为空 (即, 是否只有 head 节点) |
11. | list_is_singular | 判断链表中是否只有一个节点 (除了 head 之外) |
12. | list_cut_position | 将1个链表截断为2个链表 |
13. | list_splice | 将2个链表合并为1个链表, @list中的所有节点(不包括list)加入到 head 之后 |
14. | list_splice_tail | 将2个链表合并为1个链表, @list中的所有节点(不包括list)加入到 head 之前 |
15. | list_splice_init | 同 list_splice, 最后会初始化 @list |
16. | list_splice_tail_init | 同 list_splice_tail, 最后会初始化 @list |
No. | 主要 宏 | 说明 |
1. | list_entry | 获取包含此节点的 struct |
2. | list_first_entry | 获取包含此节点的 首个 struct |
3. | list_for_each | 从 head节点之后一个节点开始向后循环 |
4. | list_for_each_prev | 从 head节点之前一个节点开始向前循环 |
5. | list_for_each_safe | list_for_each 的安全版本, 即, 循环时即使有其它线程删除节点也可正常运行 |
6. | list_for_each_prev_safe | list_for_each_prev 的安全版本 |
7. | list_for_each_entry | 同 list_for_each, 只是参数不同 |
8. | list_for_each_entry_reverse | 同 list_for_each_prev, 只是参数不同 |
9. | list_for_each_entry_continue | 同 list_for_each_entry, 但不是从头(head)开始循环的 |
10. | list_for_each_entry_continue_reverse | 同 list_for_each_entry_reverse, 但不是从头(head)开始循环的 |
11. | list_for_each_entry_from | 从指定位置开始向后循环 |
12. | list_for_each_entry_safe | list_for_each_entry 的安全版本 |
13. | list_for_each_entry_safe_continue | list_for_each_entry_continue 的安全版本 |
14. | list_for_each_entry_safe_from | list_for_each_entry_from 的安全版本 |
15. | list_for_each_entry_safe_reverse | list_for_each_entry_reverse 的安全版本 |
1、链表的定义
struct list_head {
struct list_head *next, *prev;
}
这个不含数据域的链表,可以嵌入到任何数据结构中,例如可按如下方式定义含有数据域的链表:
struct my_list {
void * mydata;
struct list_head list;
} ;
2、链表的声明和初始化宏
struct list_head 只定义了链表结点,并没有专门定义链表头.那么一个链表结点是如何建立起来的?
内核代码 list.h 中定义了两个宏:
#defind LIST_HEAD_INIT(name) { &(name), &(name) } //仅初始化
#defind LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) //声明并初始化
如果要声明并初始化链表头mylist_head,则直接调用:LIST_HEAD(mylist_head),之后,
mylist_head的next、prev指针都初始化为指向自己。这样,就有了一个带头结点的空链表。
判断链表是否为空的函数:
static inline int list_empty(const struct list_head * head) {
return head->next == head;
} //返回1表示链表为空,0表示不空
3、在链表中增加一个结点
(内核代码中,函数名前加两个下划线表示内部函数)
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.h 中增加结点的两个函数为:
(链表是循环的,可以将任何结点传递给head,调用这个内部函数以分别在链表头和尾增加结点)
static inline void list_add(struct list_head *new, struct llist_head *head)
{
__list_add(new, head, head -> next) ;
}
static inline void list_add_tail(struct list_head 8new, struct list_head *head)
{
__list_add(new, head -> prev, head) ;
}
附:给head传递第一个结点,可以用来实现一个队列,传递最后一个结点,可以实现一个栈。
static 加在函数前,表示这个函数是静态函数,其实际上是对作用域的限制,指该函数作用域仅局限
于本文件。所以说,static 具有信息隐蔽的作用。而函数前加 inline 关键字的函数,叫内联函数,表
示编译程序在调用这个函数时,立即将该函数展开。
4、 遍历链表
list.h 中定义了如下遍历链表的宏:
#define list_for_each(pos, head) for(pos = (head)-> next ; pos != (head) ; pos = pos -> next)
这种遍历仅仅是找到一个个结点的当前位置,那如何通过pos获得起始结点的地址,从而可以引用结
点的域?list.h 中定义了 list_entry 宏:
#define list_entry( ptr, type, member ) \
( (type *) ( (char *) (ptr) - (unsigned long) ( &( (type *)0 ) -> member ) ) )
分析:(unsigned long) ( &( (type *)0 ) -> member ) 把 0 地址转化为 type 结构的指针,然后获取该
结构中 member 域的指针,也就是获得了 member 在type 结构中的偏移量。其中 (char *) (ptr) 求
出的是 ptr 的绝对地址,二者相减,于是得到 type 类型结构体的起始地址,即起始结点的地址,这样
就能读取节点里的成员。
参考代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./kernel_list.h"
struct student {
int num;
char name[20];
struct list_head list;
};
int main(void)
{
struct student *node = NULL; //node
struct list_head *pos = NULL,*n = NULL;
struct student *pnode = NULL; //
// creat
struct student *head = (struct student*)malloc(sizeof(struct student));// root
if (head == NULL) {
printf("file,%s line,%d:malloc error!\n",__FILE__,__LINE__);
exit(1);
}
INIT_LIST_HEAD(&head->list);
// insert
node = (struct student*)malloc(sizeof(struct student));
if (node == NULL) {
printf("file,%s line,%d:malloc error!\n",__FILE__,__LINE__);
exit(1);
}
node->num = 1234;
strcpy(node->name,"zhangsan");
list_add_tail(&node->list,&head->list);
node = (struct student*)malloc(sizeof(struct student));
if (node == NULL) {
printf("file,%s line,%d:malloc error!\n",__FILE__,__LINE__);
exit(1);
}
node->num = 1235;
strcpy(node->name,"lisi");
list_add_tail(&node->list,&head->list);
node = (struct student*)malloc(sizeof(struct student));
if (node == NULL) {
printf("file,%s line,%d:malloc error!\n",__FILE__,__LINE__);
exit(1);
}
node->num = 1233;
strcpy(node->name,"wangwu");
list_add(&node->list,&head->list);
//display
if (list_empty(&head->list)) {
printf("list is empty!\n");
} else {
list_for_each(pos,&head->list) {
pnode = list_entry(pos,struct student,list); // struct student * pnode= (位置 , 大结构体类型 , struct list_head list)
printf("num:%d,name %s\n",pnode->num,pnode->name);
}
}
// free
list_for_each_safe(pos,n,&head->list) {
list_del(pos);
pnode = list_entry(pos,struct student,list);
printf("num %d has removed !\n",pnode->num);
free(pnode);
}
//sort
//可用list_move实现排序
free(head);
return 0;
}
可自己参考再分块用函数实现
练习的源代码在此
改 :在main()里的 while(1)里 加
if(getchar() == '\n')
continue;
就能读取节点里的成员。
参考代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./kernel_list.h"
struct student {
int num;
char name[20];
struct list_head list;
};
int main(void)
{
struct student *node = NULL; //node
struct list_head *pos = NULL,*n = NULL;
struct student *pnode = NULL; //
// creat
struct student *head = (struct student*)malloc(sizeof(struct student));// root
if (head == NULL) {
printf("file,%s line,%d:malloc error!\n",__FILE__,__LINE__);
exit(1);
}
INIT_LIST_HEAD(&head->list);
// insert
node = (struct student*)malloc(sizeof(struct student));
if (node == NULL) {
printf("file,%s line,%d:malloc error!\n",__FILE__,__LINE__);
exit(1);
}
node->num = 1234;
strcpy(node->name,"zhangsan");
list_add_tail(&node->list,&head->list);
node = (struct student*)malloc(sizeof(struct student));
if (node == NULL) {
printf("file,%s line,%d:malloc error!\n",__FILE__,__LINE__);
exit(1);
}
node->num = 1235;
strcpy(node->name,"lisi");
list_add_tail(&node->list,&head->list);
node = (struct student*)malloc(sizeof(struct student));
if (node == NULL) {
printf("file,%s line,%d:malloc error!\n",__FILE__,__LINE__);
exit(1);
}
node->num = 1233;
strcpy(node->name,"wangwu");
list_add(&node->list,&head->list);
//display
if (list_empty(&head->list)) {
printf("list is empty!\n");
} else {
list_for_each(pos,&head->list) {
pnode = list_entry(pos,struct student,list); // struct student * pnode= (位置 , 大结构体类型 , struct list_head list)
printf("num:%d,name %s\n",pnode->num,pnode->name);
}
}
// free
list_for_each_safe(pos,n,&head->list) {
list_del(pos);
pnode = list_entry(pos,struct student,list);
printf("num %d has removed !\n",pnode->num);
free(pnode);
}
//sort
//可用list_move实现排序
free(head);
return 0;
}
可自己参考再分块用函数实现
练习的源代码在此
改 :在main()里的 while(1)里 加
if(getchar() == '\n')
continue;