本文的内容目录:
一、list_head的作用
二、list_head的定义
三、list_head的简单实现
一、list_head的作用
链表是基本的数据结构,在项目开发过程中经常会使用到。传统的链表的C语言定义大概如下所示:
/* 单向链表 */
struct list{
[成员定义]
struct list *next;
};
/* 双向链表 */
struct dList{
[成员定义]
struct dList *next;
struct dList *prev;
};
从上面两个链表的定义当中可以看出,传统的链表和数据结构本身是一体的,这样带来的好处是只要找到结点在链表中的位置就可以访问相应的结点数据;但这种定义也存在着一些问题。比如,通用性差,只能对这种类型的链表结点元素进行操作;扩展性差,每个链表元素只能建立单一链表,不能在多个链表当中共同存在,减小了链表的实际可用性等。
二、list_head的定义
list_head的定义在内核的include\linux\types.h文件中,它的实现如下:
struct list_head {
struct list_head *next, *prev;
};
从定义当中可以看出它里面只包含有两个list_head的指针。在Linux内核当中使用链表的地方是非常多的,如果每次使用链表都要定义链表结点,可以想象工作量是有多么的庞大,而且也不符合软件的设计思想(越少代码实现越多功能)。这个list_head结构体本身没有什么作用,但是将它作为一个连接件插入到具体的结构体当中,再结合http://blog.csdn.net/tech_pro/article/details/70236597这篇博文当中对container_of的使用,可以看出它构建链表的能力是多么的强大。
三、list_head的简单实现
通过编写一个测试例程来说明如何使用list_head来构造链表,以及如何来遍历这个链表。
具体的实例完整代码如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/* 找到指定的成员在定义的结构中的偏移的位置 */
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/* 根据结构体成员的地址,以及结构体成员在结构体中的偏移值计算出结构体变量的地址
* ptr : 结构体成员变量的地址
* type : 结构体的类型定义
* member : 结构体中该成员的定义名称
*/
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
/* 链表连接件的结构体定义 */
struct list_head {
struct list_head *next, *prev;
};
/* 定义一个student结构体,然后用list_head将这些结构体串起来 */
struct student{
char name[20]; //名字字段
struct list_head list; // 连接件字段
};
/* 程序的入口函数 */
int main(int argc, char *argv[])
{
int i;
struct student *stu;
struct student *p;
struct list_head *g_list = NULL; /* 表示链表头部 */
struct list_head *t_list = NULL; /* 表示临时链表元素指针 */
/* 动态分配一块内存区域 */
stu = malloc(sizeof(struct student) * 10);
if(!stu)
{
printf("malloc error!\n");
return -1;
}
/* 对内存区域进行赋值,并将其加入到定义的链表当中 */
for(i = 0; i < 10; i++)
{
sprintf(stu[i].name, "TECH-PRO - %d", i);
/* 判断链表是否为空 */
if(!g_list) /* 为空 */
{
g_list = &stu[i].list;
g_list->next = NULL;
}
else /* 不为空 */
{
t_list = g_list;
while(t_list) /* 对链表进行遍历 */
{
if(t_list->next== NULL)
{
t_list->next = &stu[i].list;
stu[i].list.next = NULL;
}
t_list = t_list->next;
}
}
}
i = 0;
while(g_list) /* 对链表进行遍历,把数据信息打印出来 */
{
p = container_of(g_list, struct student, list);
printf("%d : %s\n", i, p->name);
i += 1;
g_list = g_list->next;
}
return 0;
}
对这个测试代码进行分析:首先动态分配10个student结构体,然后对它们进行赋值,最后对它们进行遍历(通过container_of和链表头部就可以找到链表中每一项所对应的结构体变量的首地址,进而可以访问其数据成员)。
进行编译并运行,结果如下: