一些结构

本文深入探讨了Linux内核中list_head结构的实现原理,解释了其为何没有数据域,以及如何通过list_entry宏获取其父结构的地址。文章详细解析了offset和container_of宏的作用,帮助读者理解在复杂数据结构中定位特定元素的方法。

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

list_head 结构在 list.h中实现,它是一个Simple doubly linked list,我们先看一下它的优美结构:

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

这个结构看起来怪怪的,它竟没有数据域!所以看到这个结构的人第一反应就是我们怎么访问数据?

其实list_head不是拿来单独用的,它一般被嵌到其它结构中,如:

struct file_node{
        char c;
        struct list_head node;
};

此时list_head就作为它的父结构中的一个成员了,当我们知道list_head的地址(指针)时,我们可以通过list.c提供的宏 list_entry 来获得它的父结构的地址。下面我们来看看list_entry的实现:

--------------------------------
#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_entry(ptr,type,member)\
container_of(ptr,type,member)

#define offsetof(TYPE,MEMBER)     (   (size_t)& ((TYPE *)0)-> MEMBER   )
我们知道 0 地址内容是不能访问的,但 0地址的地址我们还是可以访问的, 这里用到一个取址运算符
(TYPE *)0   它表示将 0地址强制转换为TYPE类型,((TYPE *)0)-> MEMBER   也就是从0址址找到TYPE 的成员MEMBER 。

将实参代入 offset( struct file_node, node );最终将变成这样:
    (   (size_t) & ((struct file_node*)0)-> node );这样看的还是不很清楚,我们再变变:
struct file_node *p = NULL;
& p->node;

这样应该比较清楚了,即求 p 的成员 node的地址,只不过p 为0地址,从0地址开始算成员node的地址,也就是 成员 node 在结构体 struct file_node中的偏移量offset宏就是算MEMBERTYPE中的偏移量的。

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

这个宏是由两个语句组成,最后container_of返回的结果就是第二个表达式的值。这里__mptr中间变量,这就是list_head指针类型,它被初始化为ptr的值,而ptr就是当前所求的结构体中list_head节点的地址。为什么要用中间变量,这是考虑到安全性因素,如果传进来一个ptr++,所有ptr++放在一个表达式中会有副作用,像 (p++)+(p++)之类。
(char*)__mptr 之所以要强制类型转化为char是因为地址是以字节为单位的,而char的长度就是一个字节。
container_of的值是两个地址相减,
刚说了__mptr是结构体中list_head节点的地址,offset宏求的是list_head节点MEMBER在结构体TYPE中的偏移量,那么__mptr减去它所在结构体中的偏移量,就是结构体的地址。

所以list_entry(ptr,type,member)宏的功能就是,由结构体成员地址求结构体地址。其中ptr 是所求结构体中list_head成员指针,type是所求结构体类型,member是结构体list_head成员名。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值