container_of宏剖析

container_of宏剖析
//该宏位于include/linux/kernel.h

1.定义格式

/**

* container_of - cast a member of a structure out to the containing structure

*

* @ptr: the pointer to the member.

* @type: the type of the container struct this is embedded in.

* @member:the name of the member within the struct.

*

*/

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

const typeof( ((type *)0)->member ) *__mptr = (ptr); \

(type *)( (char *)__mptr - offsetof(type,member) );})

作用:就是根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。

例:

struct demo_struct

{

type1 member1;

type2 member2;

type3 member3;

}

struct demo_struct demo1,*pdemo;

type2 * demo_member2=demo1.member2;

如果要得到demo1的指针,可以使用该宏:

pdemo=container_of(demo_member2,struct demo_struct,member2);

2.宏运行机理解析

typeof是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型。将上例中的宏按照宏定义进行展开,如下:

1 pdemo=({\

2 const typeof(((struct demo_struct *)0)->member2) *__mptr=(demo_member2); \

3 (struct demo_struct *)((char *)__mptr-offsetof(struct demo_struct, member2));\

4 })

从上面定义来看,代码中的第2行的作用是首先使用typeof获取结构体域变量member2的类型为type2,然后定义了一个type2指针类型的临时变量__mptr,并将实际结构体变量中的域变量的指针demo_member2的值赋给临时变量__mptr。

第2行代码实际上类似下面定义:

const type2 * __mptr=demo_member2;

这里((struct demo_struct *)0)比较巧妙,它指的是struct demo_struct型变量地址为基地址,偏移量为0的地址,实际上就是struct demo_struct型变量地址。

第3行代码中,(char *)__mptr转换为字节型指针。(char *)__mptr - offsetof(type,member) )用来求出结构体起始地址(为char *型指针),然后(type *)( (char *)__mptr - offsetof(type,member) )在(type *)作用下进行将字节型的结构体起始指针转换为type *型的结构体起始指针。

其中,offsetof宏定义如下:

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

可以看出,该宏就是计算出TYPE变量中MEMBER成员基地址。该宏运行机理如下:

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

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

l &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址;

l (size_t)(&(((TYPE*)0)->MEMBER))结果转换类型。

该宏巧妙之处在于将0转换成(TYPE*),如果结构体以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;

__mptr - offsetof(struct demo_struct, member2)

type1 member1

type2 member2

type3 member3

offsetof(type,member)__mptr

__mptr - offsetof(struct demo_struct, member2)

type1 member1

type2 member2

type3 member3

offsetof(type,member)__mptr

还有一篇文章

在linux内核中经常可以看到container_of的身影,也是linux引以为豪的地方之一了。《linux设备驱动开发详解》132页对container_of的作用作了说明——通过结构体成员的指针找到这个成员所在结构体的指针。但没有具体分析它是怎么实现的。

下面我们先看看这个宏的定义:

/**

* container_of - cast a member of a structure out to the containing structure

* @ptr: the pointer to the member.

* @type: the type of the container struct this is embedded in.

* @member: the name of the member within the struct.

*

*/

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

const typeof( ((type *)0)->member ) *__mptr = (ptr); \

(type *)( (char *)__mptr - offsetof(type,member) );})

参数ptr是结构体typ的成员member的指针,我们很多时候希望得到结构体type的起始地址,也就是type的指针。

假设这个type在内存中的存储模型如下:

type
|----------|
| |
| |
|----------|

ptr->| member --|
|----------|
| |
| |
|----------|

这里,我们拆开来就好理解了:

首先,(type *)0)是把0地址转化为TYPE结构的指针(这里把0换成其它值也是一样的);

((type *)0)->member type结构体中的member成员;

typeof( ((type *)0)->member ) 返回member的类型;

const typeof( ((type *)0)->member ) *__mptr = (ptr); 用上面这个类型定义一个指针__mptr,并把ptr赋值给它;

(char *)__mptr 把__mptr转化成char型指针;

offsetof宏定义在[include/linux/stddef.h]中定义为:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
这里,说明一下这个宏,((size_t) &((TYPE *)0)->MEMBER)把0地址转化为TYPE结构的指针,然后获取该结构中MEMBER成员的指针,并将其强制转换为size_t类型。于是,由于结构从0地址开始定义,因此,这样求出的MEMBER成员地址,实际上就是它在结构中的偏移量。这也显示出了C语言中指针的强大。因为,在某个体系结构下实现的libc,结构中各个成员的偏移总是可以预见的。

现在有了member成员在type结构体中的偏移量,又有了指向member的指针__mptr,自然就可以计算出type结构的起始地址了。
小小一个宏就包括了这么多精华,可见linux的博大。

时间: 2024-09-30 14:17:05

container_of宏剖析的相关文章

深入浅出实例解析linux内核container_of宏

做一件事情首先应该知道它的目的是什么. container_of的目的:如何通过结构中的某个变量获取结构本身的指针. 总体思路:假想一下,你的结构体中有好几个成员,你如何通过里面的"任一成员"获取整个结构体的首地址呢.container_of的做法就是通过typeof定义一个与"任一成员"同类型的指针变量pvar_a(假设变量名就是pvar_a),并让指针变量pvar_a指向这个"任一成员",然后用 "pvar_a的地址" 减

第十七天:APCS和container_of宏

APCS 全称:ARM 过程调用标准 如果要写用来与编译后的 C 连接的汇编代码,则必须使用 APCS. 今天的课程最终的两个目标:使用符合APCS标准的汇编写输出hello world  以及编写container_of宏 .这两个的推导过程比较复杂和具有跳跃性.结论的话要记住两个知识点,一:编写汇编函数的时候要对相应的寄存器进行保护.保护代码需要记住,每次写函数的时候都要用到.二:container_of实现了根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针的功能.以

C语言之offset_of宏和container_of宏

通过结构体整体变量来访问其中各个元素,本质上是通过指针方式来访问的,形式上是通过.的方式来访问的(这时候其实是编译器帮我们自动计算了偏移量). 1:offset_of宏 作用:计算结构体中某个元素和结构体首地址的偏移量(其实质是通过编译器来帮我们计算). 定义: #define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER) 参数分析:TYPE是结构体类型,MEMBER是结构体中一个元素的元素名 返回值:member元素相对于整个结构体

C语言之offsetof宏和container_of宏

首先我们要明白一点通过结构体变量来访问结构体中的各个元素时,其本质上是 通过指针的方式来实现访问的,只不过是这个时候编译器帮自动帮我们计算了每个 元素与结构体起始地址之间的偏移量而已 一:offsetof宏: #define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER) 1:参数与返回值分析: (1)TYPE是结构体类型,MEMBER是结构体中一个元素的元素名 (2)这个宏返回的是member元素相对于整个结构体变量的首地址的偏移量,

container_of宏定义解析

container_of宏,定义kernel.h中: /** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within th

对offsetof、 container_of宏和结构体的理解

offsetof 宏 #include<stdio.h> #define offsetoff(type, member)      ((int)&((type*)0)->member) /* ((type*)0)->member 释义:声明一个相应类型的结构体指针,该指针指向0地址处.再通过该指针访问各元素.我们只要获取一个指针就能访问其内部的元素吗,可以这么搞?其实我是想联系全局和非全局变量,通过上面这个代码也许你不明白我要表达的意思.请继续慢慢看,直到本文后面,结合本文

container_of()宏

最近学习为Android添加编写内核驱动,遇到的第一个问题就是linux中大名鼎鼎的container_of()宏. container_of()是Linux内核中一个非常重要的宏,主要功能就是:根据结构体中一个域成员的地址获取整个结构体的地址. container_of()定义如下: 1 /** 2 * container_of - cast a member of a structure out to the containing structure 3 * @ptr: the pointe

linux中offsetof与container_of宏定义

linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER)    ((size_t) &((TYPE *)0)->MEMBER) /** * container_of - cast a member of a structure out to the containing structure * @ptr:        the pointer to the member. * @type:       the type

offsetof与container_of宏分析

offsetof宏:结构体成员相对结构体的偏移位置 container_of:根据结构体成员的地址来获取结构体的地址 offsetof 宏 原型: #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) (TYPE *)0非常巧妙,告诉编译器有一个指向结构体 TYPE 的指针,其地址是0,然后取该指针的 MEMBER 地址 &((TYPE *)0)->MEMBER,因为基址是0,所以这时获取到的 MEMBER