您的位置:首页 > 理论基础 > 数据结构算法

Linux内核数据结构之链表

2013-09-10 21:43 246 查看

链表

在学习《Linux内核设计与实现》中链表结构时,对<scripts\kconfig\list.h>(我看的内核是2.6的,在其他版本对链表的定义可能不在这个目录中)中的宏定义:container_of()很困惑,所以google一番,现记录自己对其的理解:

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})


宏定义的三个参数的含义分别为:

type:一般是个结构体,也就是包含用户数据和链表节点的结构体

ptr:是指向type中链表节点的指针

member:是type中定义链表节点时用的名字

比如:

struct student
{
int id;
char* name;
struct list_head list;
};

type是struct student
ptr是指向struct list_head的指针,也就是指向member类型的指针
member就是list
对于container_of宏:

// 步骤1:将数字0强制转型为type*,然后取得其中的member元素
((type *)0)->member  // 相当于((struct student *)0)->list

// 步骤2:定义一个临时变量__mptr,并将其也指向ptr所指向的链表节点
const typeof(((type *)0)->member)*__mptr = (ptr);

// 步骤3:计算member字段距离type中第一个字段的距离,也就是type地址和member地址之间的差
// offset(type, member)也是一个宏,定义如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

// 步骤4:将__mptr的地址 - type地址和member地址之间的差
// 其实也就是获取type的地址

步骤1很好理解,就是强制类型转换。
步骤2中的typeof()的用法说明:

//把y定义成x指向的数据类型:
typeof(*x) y;

步骤3:(TYPE *)0,将 0 强制转换为 TYPE 型指针,记 p = (TYPE *)0,p是指向TYPE的指针,它的值是0。那么 p->MEMBER 就是 MEMBER 这个元素了,而&(p->MEMBER)就是MENBER的地址,而基地址为0,这样就巧妙的转化为了TYPE中的偏移量。再把结果强制转换为size_t型的就OK了,size_t其实也就是int。

typedef __kernel_size_t  size_t;

typedef unsigned int __kernel_size_t;   

步骤4就是为了获得type的地址。

通过container_of()宏,我们定义一个简单的函数就可以返回包含list_head的父类型结构体:

#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

因为我们获得一个指向链表结构的指针通常是无用的,我们要操作的是用户数据,是一个指向包含list_head的结构体指针,如之前的student结构体。而通过宏定义container_of()就可以让我们获得该结构体的首地址。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: