container_of 的用法

时间:2022-12-17 16:05:51
1.问题:如何通过结构中的某个变量获取结构本身的指针???
关于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 the struct.
*
*/
#define container_of(ptr, type, member) ({ / const typeof( ((type *)0)->member ) *__mptr = (ptr); / (type *)( (char *)__mptr - offsetof(type,member) );}) 

container_of在Linux Kernel中的应用非常广泛,它用于获得某结构中某成员的入口地址.

关于offsetof见stddef.h中:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
TYPE是某struct的类型 0是一个假想TYPE类型struct,MEMBER是该struct中的一个成员. 由于该struct的基地址为0, MEMBER的地址就是该成员相对与struct头地址的偏移量.
关于typeof,这是gcc的C语言扩展保留字,用于声明变量类型.
const typeof( ((type *)0->member ) *__mptr = (ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr.
(type *)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针. 该指针就是member的入口地址了. 例一;
container_of宏定义在[include/linux/kernel.h]中:
#define container_of(ptr, type, member) / const typeof( ((type *)0)->member ) *__mptr = (ptr); / (type *)( (char *)__mptr - offsetof(type,member) ); 

offsetof宏定义在[include/linux/stddef.h]中:
#define offsetof(type, member) ((size_t) &((type *)0)->member) 

下面用一个测试程序test.c来说明
#include struct student{ char name[20]; char sex; }stu={"zhangsan",'m'}; main() { struct student *stu_ptr; //存储container_of宏的返回值 int offset; //存储offsetof宏的返回值 //下面三行代码等同于 container_of(&stu.sex,struct student, sex )参数带入的情形 const typeof(((struct student*)0)->sex) *_mptr = &stu.sex; //首先定义一个 _mptr指针, 类型为struct student结构体中sex成员的类型 //typeof 为获取(((struct student*)0)->sex)的类型,此处此类型为char //((struct student*)0)在offsetof处讲解 offset = (int)(&((struct student *)0)->sex); /*((struct student*)0)为 把 0地址 强制转化为指向student结构体类型的指针 该指针从地址 0 开始的 21个字节用来存放name 与 sex(char name〔20〕与 char sex共21字节) sex存放在第20个字节出(从0字节开始) &((struct student *)0)->sex 取出sex地址(此处即为20) 并强制转化为整形 所以offset为20,后面的printf结果将证明这一点*/ stu_ptr = (struct student *)((char*)_mptr - offset); /*((char*)_mptr - offset)此处先把_mptr指针转化为字符形指针 ( 为什么这么做呢? 如果_mptr为整形指针 _mptr - offset 相当于减去 sizeof(int)*offset个字节) 减去 offset值 相当于 得到_mptr所在结构体的首地址(即stu的地址) 然后我们把 该地址 强制转化为 struct student类型即可正常使用了*/ printf("offsetof stu.sex = %d/n",offset); printf("stu_ptr->name:%s/tstu_ptr->sex:%c/n", stu_ptr->name, stu_ptr->sex); return 0; } 

例二: 
它的作用显而易见,那就是根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。比如,有一个结构体变量,其定义如下:

 struct demo_struct { type1 member1; type2 member2; type3 member3; type4 member4; }; struct demo_struct demo; 

同时,在另一个地方,获得了变量demo中的某一个域成员变量的指针,比如: 

   type3 *memp = get_member_pointer_from_somewhere(); 
此时,如果需要获取指向整个结构体变量的指针,而不仅仅只是其某一个域成员变量的指针,我们就可以这么做: 

   1. struct demo_struct *demop = container_of(memp, struct demo_struct, member3); 
首先,我们将container_of(memp, struct demo_struct, type3)根据宏的定义进行展开如下:     struct demo_struct *demop = ({ / 
 const typeof( ((struct demo_struct *)0)->member3 ) *__mptr = (memp); / (struct demo_struct *)( (char *)__mptr - offsetof(struct demo_struct, member3) );}) 

其中,typeof是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型。因此,上述代码中的第2行的作用是首先使用typeof获取结构体域变量member3的类型为 type3,然后定义了一个type3指针类型的临时变量__mptr,并将实际结构体变量中的域变量的指针memp的值赋给临时变量__mptr。 

假设结构体变量demo在实际内存中的位置如下图所示: 
     demo 
 +-------------+ 0xA000 
 |   member1              | 
 +-------------+ 0xA004 
 |   member2             | 
 |                                | 
 +-------------+ 0xA010 
 |   member3             | 
 |                                | 
 +-------------+ 0xA018 
 |   member4             | 
 +-------------+  则,在执行了上述代码的第2行之后__mptr的值即为 0xA010。  再看上述代码的第3行,其中需要说明的是offsetof,它定义在include/linux/stddef.h中,其定义如下: 
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 

 
同样,我们将上述的offsetof调用展开,即为:     (struct demo_struct *)( (char *)__mptr - ((size_t) &((struct demo_struct *)0)->member3) ); 

可见,offsetof的实现原理就是取结构体中的域成员相对于地址0的偏移地址,也就是域成员变量相对于结构体变量首地址的偏移。 

因此,offsetof(struct demo_struct, member3)调用返回的值就是member3相对于demo变量的偏移。结合上述给出的变量地址分布图可知,offsetof(struct demo_struct, member3) 将返回0x10。 

于是,由上述分析可知,此时,__mptr==0xA010,offsetof(struct demo_struct, member3)==0x10。 
因此, (char *)__mptr - ((size_t) &((struct demo_struct *)0)->member3) == 0xA010 - 0x10 == 0xA000,也就是结构体变量demo的首地址(如上图所示)。  由此,container_of实现了根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针的功能。

container_of 的用法的更多相关文章

  1. linux中container_of

    linux 驱动程序中 container_of宏解析 众所周知,linux内核的主要开发语言是C,但是现在内核的框架使用了非常多的面向对象的思想,这就面临了一个用C语言来实现面向对象编程的问题,今天 ...

  2. linux内核编程笔记【原创】

    以下为本人学习笔记,如有转载请注明出处,谢谢 DEFINE_MUTEX(buzzer_mutex); mutex_lock(&buzzer_mutex); mutex_unlock(& ...

  3. container_of用法及实现

    container_of 有的情况下,只知道 struct结构中莫个成员的指针,而需要知道整个struct的指针 (如网卡驱动里面,list) struct DDD {         int a; ...

  4. 刨一刨内核container_of()的设计精髓

    新年第一帖,总得拿出点干货才行,虽然这篇水分还是有点大,大家可以晒干了温水冲服.这段时间一直在整理内核学习的基础知识点,期间又碰到了container_of()这个宏,当然还包括一个叫做offseto ...

  5. container_of

    在学习Linux驱动的过程中,遇到一个宏叫做container_of.该宏定义在include/linux/kernel.h中,首先来贴出它的代码: /** * container_of - cast ...

  6. container_of分析【转】

    转自:http://blog.csdn.net/tigerjibo/article/details/8299589 1.container_of宏 1> Container_of在Linux内核 ...

  7. 嵌入式C语言自我修养 04:Linux 内核第一宏:container_of

    4.1 typeof 关键字 ANSI C 定义了 sizeof 关键字,用来获取一个变量或数据类型在内存中所占的存储字节数.GNU C 扩展了一个关键字 typeof,用来获取一个变量或表达式的类型 ...

  8. container_of分析--可用good【转】

    转自:http://blog.csdn.net/tigerjibo/article/details/8299589 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.container_of宏 ...

  9. 关于container_of和list_for_each_entry 及其相关函数的分析

    Linux代码看的比较多了,经常会遇到container_of和list_for_each_entry,特别是 list_for_each_entry比较多,因为Linux经常用到链表,虽然知道这些函 ...

随机推荐

  1. 探究toString()和valueOf()

    1.用法如下:toString()方法:返回对象的字符串表示. 对象 操作 Array 将 Array 的元素转换为字符串.结果字符串由逗号分隔,且连接起来. Boolean 如果 Boolean 值 ...

  2. GTD桌面2.0

    在以前实践了一个GTD桌面,当时称为1.0版本,当时的效果是这样的: 2015年更换一点设备,把GTD桌面升级一下,就称为2.0吧.直接上图: 可以发现显示器由以前的1台又变回2台,原以为1台大显示器 ...

  3. 百度editor调用【图片上传阿里云】

    百度editor调用简单,但是图片和文件上传阿里云就有点难度了.下面我详细说一下. 百度富文本编辑器下载地址:http://ueditor.baidu.com/website/download.htm ...

  4. AutoLayout没有相对比例布局

    怎么实现相对比例布局 比如我一个控件相对上边距的位置在整个屏幕的比例 可以用stack view来管理相对布局

  5. yii2.0下拉列表的使用

    第一种方法:ActiveForm 类的 dropDownList 方法(优点,默认使用yii的样式) 1.在控制器的方法里面 ,我们需要拿到数据,一定是 findAll() 或者是 all() 方法的 ...

  6. 手动整合实现SSH项目开发01

    内容简介:本文主要介绍SSH项目开发的配置以及简单登录功能的实现. 1. 新建一个Dynamic Web Project. 2.导入需要 的Jar包,此项目是Struts.Hibernate.Spri ...

  7. Maven 学习总结 (一)

    一.何为Maven 1.Maven是优秀的构建工具 maven的用途之一是用于构建,他是一个强大的构建工具,能够帮助我们自动化构建过程,从清理.编译.测试到生成报告,再到打包和部署. 他抽象了一个完整 ...

  8. python中dict的fromkeys用法

    fromkeys是创造一个新的字典.就是事先造好一个空字典和一个列表,fromkeys会接收两个参数,第一个参数为从外部传入的可迭代对象,会将循环取出元素作为字典的key值,另外一个参数是字典的val ...

  9. iOS:PSTCollectionView

    https://github.com/steipete/PSTCollectionView Open Source, 100% API compatible replacement of UIColl ...

  10. iTween的用法总结

    Unity3D插件-iTween的基本用法 本文提供全流程,中文翻译.Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) 1 Introd ...