momo zone

调核人的blog

container_of 宏分析

用法:

各参数含义:

type — 需要操作的数据类型,通常为结构;

member — type结构的成员名称;

ptr — member类型定义的指针变量;
例如:

struct my_t{

int mem0;

float mem2;

double mem4;

char  mem6;
};

struct my_t abc, *pabc;

abc.mem2 = 0.0;

float *pmem2 = &abc.mem2; //已知

//通过成员变量的地址得到它所在结构的首地址
pabc = container_of(pmem2 , struct my_t, mem2 );

这个宏在我读驱动程序的时候经常见到,其功能就是通过一个结构中的成员找到包含这个成员的结构。
这个绝对属于奇技淫巧,不属于正常编程的方法。
现看看他的定义:
include/linux/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 the struct.
 *
 */
#define container_of(ptr, type, member) ({
const typeof(((type *)0)->member) * __mptr = (ptr);
(type *)((char *)__mptr – offsetof(type, member)); })
#endif

ok 看到了offsetof 就先从他开始讲:
include/linux/stddef.h:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

(TYPE *)0 //将0转换成TYPE型指针
(TYPE *)0->MEMBER //访问TYPE型里的成员MEMBER
&((TYPE *)0->MEMBER) //取出该成员的地址
(size_t) &((TYPE *)0->MEMBER) //将该成员的地址值转换为size_t类型
说白了这个就是得到一个成员在结构体中的偏移
接下来再回过头看container_of:
type:结构体类型
member:结构体成员
ptr:结构体成员指针
typeof(((type *)0)->member) //获得成员变量的类型(原型)
const typeof(((type *)0)->member) *__mptr=(ptr) //用这个成员变量的类型定义一个值为ptr的指针__mptr(注意这里其实是指针赋值)
((char *)__mptr – offsetof(type, member)) //再把这个指针转化为字节指针(char类型)减去成员在结构体中的偏移
由于__mptr 指向的其实就是成员的绝对地址,减去成员在结构体内的偏移地址,得到的就是包哈该成员的结构体的绝对地址! 
(type *)((char *)__mptr – offsetof(type, member)) //再修正一下,把绝对地址转换为type类型(不这样的话不够严谨啊)
最后外面的一层{} 就是返回{}包裹的语句中最后一行的值

其实这个宏在内核中还有另一个定义:
#define container_of(obj, type, memb)
((type *)(((char *)obj) – offset_of(type, memb)))
这个就简单多了,省掉了__mptr,好像也没有什么问题
猜下原因吧(引用别人的):
linux kernel的开发者是认真严谨的程序员,定义__mptr是用来防止宏的副作用。container_of宏的第一个参数是指针变量,如果我们这样使用宏:container_of(p_member++, struct sample_struct, member_name),那么如果ptr在container_of宏中出现两次,那么ptr就自增了两次!多么危险的副作用!当然实际上container_of宏中ptr只出现了一次,但是认真严谨的程序员为了防止可能出现的可怕的副作用,习惯性的加上了这样一个常量。
Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: