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的博大。

container_of宏剖析的更多相关文章

  1. Linux内核中container_of宏的详细解释

    上一节拒绝造轮子!如何移植并使用Linux内核的通用链表(附完整代码实现)我们在分析Linux内核链表的时候注意到内核在求解结构体偏移的时候巧妙的使用了container_of宏定义,今天我们来详细剖 ...

  2. linux中offsetof与container_of宏定义

    linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER)    ((size_t) &((TYPE *)0)->M ...

  3. (转)offsetof与container_of宏[总结]

    1.前言 今天在看代码时,遇到offsetof和container_of两个宏,觉得很有意思,功能很强大.offsetof是用来判断结构体中成员的偏移位置,container_of宏用来根据成员的地址 ...

  4. container_of宏定义分析---linux内核

    问题:如何通过结构中的某个变量获取结构本身的指针??? 关于container_of宏定义在[include/linux/kernel.h]中:/*_** container_of - cast a ...

  5. 内核中container_of宏的详细分析【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637597.html 内核中container_of宏的详细分析 16年2月28日09:00:37 内核中 ...

  6. offsetof与container_of宏[总结]

    1.前言 今天在看代码时,遇到offsetof和container_of两个宏,觉得很有意思,功能很强大.offsetof是用来判断结构体中成员的偏移位置,container_of宏用来根据成员的地址 ...

  7. container_of宏

    title: container_of宏 date: 2019/7/24 15:49:26 toc: true --- container_of宏 解析 在linux链表结构中有这样一个宏,通过成员变 ...

  8. offsetof与container_of宏分析

    offsetof宏:结构体成员相对结构体的偏移位置 container_of:根据结构体成员的地址来获取结构体的地址 offsetof 宏 原型: #define offsetof(TYPE, MEM ...

  9. typeof, offsetof, container_of宏

    container_of宏实现如下: #define container_of(ptr, type, member) ({ \ )->member ) *__mptr = (ptr); \ (t ...

随机推荐

  1. ScrollView 尽量避免嵌套RelativeLayout,非常惨痛的教训

    <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android=&q ...

  2. 24种设计模式--原型模式【Prototype Pattern】

    今天我们来讲原型模式,这个模式的简单程度是仅次于单例模式和迭代器模式,非常简单,但是要使用好这个模式还有很多注意事项.我们通过一个例子来解释一下什么是原型模式. 现在电子账单越来越流行了,比如你的信用 ...

  3. bm25

    BM25算法,通常用来作搜索相关性平分.一句话概况其主要思想:对Query进行语素解析,生成语素qi:然后,对于每个搜索结果D,计算每个语素qi与D的相关性得分,最后,将qi相对于D的相关性得分进行加 ...

  4. nodejs -formidable模块实现图片上传。

    var form = new formidable.IncomingForm(); form.uploadDir="/localnonobank/test/images/";   ...

  5. python連接mysql數據庫

    第一步,安裝mysql數據庫. 這裏我安裝的是mariadb數據庫,版本5.5,並且配置好了字符集.此處不詳細敘述,相信大家沒有問題. 第二步,安裝mysql驅動. 首先說明一下有兩個主要的驅動: m ...

  6. CentOS使用sudo提示用户不在sudoers文件中的解决方法

    1切换到root用户[linux@localhost ~]$ su root密码:[root@localhost ~]# 2查看/etc/sudoers文件权限,如果只读权限,修改为可写权限 [roo ...

  7. swift swizzle

    SWIZZLE 由 王巍 (@ONEVCAT) 发布于 2015/09/30 Swizzle 是 Objective-C 运行时的黑魔法之一.我们可以通过 Swizzle 的手段,在运行时对某些方法的 ...

  8. cocos2d-x笔记5: 通过jni实现C++调用Java

    Cocos2d-x的跨平台性很强大,但是偶尔也需要平台的原生API结合. C++在Win32平台下简单的很,C++可以直接用MFC或者调用Win32API. Ios在XCode下直接就能C++和OC混 ...

  9. iOS 图片填充 UIImageView

    UIViewContentModeScaleAspectFit,     //这个图片都会在view里面显示,并且比例不变  这就是说 如果图片和view的比例不一样 就会有留白如下图1 UIView ...

  10. Ansj分词双数组Trie树实现与arrays.dic词典格式

    http://www.hankcs.com/nlp/ansj-word-pairs-array-tire-tree-achieved-with-arrays-dic-dictionary-format ...