您的位置:首页 > 运维架构 > Linux

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

2016-08-04 20:30 381 查看


define container_of(ptr, type, member) ({ \

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

作用是这样的:如果你获得了一个大结构体里面的一个成员的指针,那么通过这个宏,你就能获得这个结构体的指针。
假设存在这么一个结构体: 

Struct student{ 

Type1 A; 

Type2 B; 

Type3 C; 

}; 

这里的type1,2,3就是我们平时常见的int,char之类的。之所以不写具体的类型是避免还要考虑内存中字节对齐等问题。 

现在用这个结构体来声明一个对象:struct student demo; 

那么在内存中demo的分布应该是这样的: 

—————–0x6000 



—————- 0x6008 



—————–0x600a 



—————-0x600e 

上面的内存地址分布都是我随意写的,因为我并没有具体type1.2.3是什么类型。 

那么container_of宏的三个参数分别代表什么呢? 

Ptr :指向结构体内成员的指针,比如type2 * pointer=&B 

Type :这个结构体类型 ,比如 struct student 

Member:结构体成员名字,比如 B 

代入宏看看是什么样的?


define container_of(pointer, struct student, B) ({ \

const typeof( (( struct student*)0)->B) *__mptr = (pointer);    \
( struct student*)( (char *)__mptr - offsetof( struct student,B) );})

初步的替换就是这样,但是这个里面还有宏offsetof(struct student,B),暂且不看先。 

const typeof( (( struct student*)0)->B) *__mptr = (pointer); 这句代码的作用是声明了一个指针__mptr。Typeof的作用是获取变量的类型,所以__mptr的类型就是type2。然后把pointer指针的值赋给它,其实就是两个指针指向同一块内存,就是指向0x6008。 

下面展开offsetof( struct student,B) 看看是什么?


define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

替换后得到:


define offsetof(struct student, B) ((size_t) &((struct student*)0)->B)

(struct student*)0)这句话的意思是把student结构体的起始地址强制制定为0,那么B的地址就是相对0地址的偏移量,然后取址,在上图中就是0x0008. 

回看(char )__mptr这句话的意思:把这个指针强制类型转换为(char),也就是这个指针现在是字节指针了。为什么这么做呢?因为内存存储的最基本单位都是字节。这样的转换可以用来做加减法十分方便。 

综上所述,0x6008-0x0008=0x6000.这个地址也就是demo结构体的起始地址了,然后转换为struct student类型的指针。

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

先分析一下这个 宏的运行机理:

一共4步 

1. ( (TYPE *)0 ) 将零转型为TYPE类型指针; 

2. ((TYPE *)0)->MEMBER 访问结构中的数据成员; 

3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址; 这个实现相当于获取到了 MEMBER 成员相对于其所在结构体的偏移,也就是其在对应结构体中的什么位置。

4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型。巧妙之处在于将0转 换成(TYPE*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;

&操作如果是对一个表达式,而不是一个标识符,会取消操作,而不是添加。

比如&*a,会直接把a的地址求出来,不会访问*a。

&a->member,会把访问a->member的操作取消,只会计算出a->member的地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux kernel