1. #define list_entry(ptr, type, member) container_of(ptr, type, member)

在进行编程的时候,我们经常在知道结构体地址的情况下,寻找其中某个成员的地址;但是知道了成员的地址,如果找到这个结构体对应的地址呢?
Linux内核中,获取节点地址的函数是list_entry(),它的宏定义如上所示。
我们再来查找container_of(ptr, type, member)的定义,发现它依然是一个宏定义:
  1. #define container_of(ptr, type, member) \
  2. ({ \
  3. consttypeof(((type *)0)->member)* __mptr =(ptr); \
  4. (type *)((char*)__mptr - offsetof(type, member)); \
  5. })
在container_of(ptr, type, member)的宏定义中,真正返回节点地址的是最后一句话,
而在最后一句话中offsetof(TYPE, MEMBER)依然是一个宏定义。
  1. #define offsetof(TYPE, MEMBER)((size_t)&((TYPE *)0)->MEMBER)
  1. typedef__kernel_size_tsize_t;
  2. typedefunsignedint__kernel_size_t;
通过逐层查找之后我们来说一下list_entry()函数的具体实现,我们从下往上说起。
 
 
    1. #define offsetof(TYPE, MEMBER)((size_t)&((TYPE *)0)->MEMBER)
  • TYPE
这是我们自定义的结构体类型,它的内部至少一个list_head型成员变量,如下:
  1. struct TYPR
  2. {
  3. //...
  4. struct list_head member;
  5. //...
  6. };
其中list_head也是一个结构体,它的定义我们稍后再说。
  • MEMBER
这是TYPE对象中list_head型变量的变量名。
  • 语句解析
(TYPE *)0:将0强制转换成TYPE型指针,则该指针一定指向0地址(数据段基址)。
&((TYPE *)0)->MEMBER这句话其实是&(((TYPE *)0)->MEMBER),通过该指针访问TYPE的MEMBER成员并得到其地址。
由于该指针的起始地址是0,那么&((TYPE *)0)->MEMBER也就是一个TYPE型变量的起始地址
与该变量内部MEMBER成员变量起始地址之间的偏移量,这个偏移量对于所有的TYPE型变量都是成立的。
那么,接下来的思路便很明确了,我们只要知道一个TYPE类型变量中MEMBER变量的起始地址,减去
offsetof(TYPE, MEMBER)这个偏移量,就可以得到TYPE类型变量的起始地址。
它的的对应关系如下图所示:

 
思路很清晰,但还有一些细节需要注意,我们继续看代码。
 
2.
  1. #define container_of(ptr, type, member) \
  2. ({ \
  3. consttypeof(((type *)0)->member)* __mptr =(ptr); \
  4. (type *)((char*)__mptr - offsetof(type, member)); \
  5. })
  • const typeof(((type *)0)->member) * __mptr = (ptr);
由于下面我们要对指针进行强制类型转换,所以这里我们又申请一个指针,指向和ptr相同的位置。
这里的ptr指的是实际list_head member的地址。
  • (char *)__mptr
由于offsetof()函数求得的是偏移字节数,所以这里(char *)__mptr使得指针的加减操作步长为1Byte
然后二者相减便可以得到TYPE变量的起始地址,最后通过(type *)类型转换,将该地址转换为TYPE类型的指针。
再向上就是一些宏定义没有什么可说的了。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

由结构体成员地址计算结构体地址——list_entry()原理详解的更多相关文章

  1. 节点地址的函数list_entry()原理详解

    本节中,我们继续讲解,在linux2.4内核下,如果通过一些列函数从路径名找到目标节点. 3.3.1)接下来查看chached_lookup()的代码(namei.c) [path_walk()> ...

  2. IP地址、子网掩码详解

    如何通过子网掩码划分网段 资料一: 一.缺省A.B.C类地址,子网掩码:  二.子网掩码的作用:  code:  IP地址 192.20.15.5 11000000 00010100 00001111 ...

  3. CentOS 最新版的下载地址 + 版本选择详解

    CentOS 最新版的下载地址 + 版本选择详解 发现越来越多的机关单位.事业单位开始使用 Linux 作为主要服务器,毕竟,Linux的稳定性和高效性是众所周知的,所以我也打算把自己这一块技术加强一 ...

  4. 【转】C语言中不同的结构体类型的指针间的强制转换详解

    C语言中不同类型的结构体的指针间可以强制转换,很自由,也很危险.只要理解了其内部机制,你会发现C是非常灵活的. 一. 结构体声明如何内存的分布, 结构体指针声明结构体的首地址, 结构体成员声明该成员在 ...

  5. 硬盘内部硬件结构和工作原理详解[zz]

    一般硬盘正面贴有产品标签,主要包括厂家信息和产品信息,如商标.型号.序列号.生产日期.容量.参数和主从设置方法等.这些信息是正确使用硬盘的基本依据,下面将逐步介绍它们的含义. 硬盘主要由盘体.控制电路 ...

  6. 交换机工作原理、MAC地址表、路由器工作原理详解

    一:MAC地址表详解 说到MAC地址表,就不得不说一下交换机的工作原理了,因为交换机是根据MAC地址表转发数据帧的.在交换机中有一张记录着局域网主机MAC地址与交换机接口的对应关系的表,交换机就是根据 ...

  7. java Spring系列之 配置文件的操作 +Bean的生命周期+不同数据类型的注入简析+注入的原理详解+配置文件中不同标签体的使用方式

    Spring系列之 配置文件的操作 写在文章前面: 本文带大家掌握Spring配置文件的基础操作以及带领大家理清依赖注入的概念,本文涉及内容广泛,如果各位读者耐心看完,应该会对自身有一个提升 Spri ...

  8. 小甲鱼PE详解之IMAGE_OPTIONAL_HEADER32 结构定义即各个属性的作用(PE详解03)

    咱接着往下讲解IMAGE_OPTIONAL_HEADER32 结构定义即各个属性的作用! (视频教程:http://fishc.com/a/shipin/jiemixilie/) 接着我们来谈谈 IM ...

  9. 小甲鱼PE详解之IMAGE_NT_HEADERS结构定义即各个属性的作用(PE详解02)

    PE Header 是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,里边包含着许多PE装载器用到的重要字段.下边小甲鱼将为大家详细讲解哈~ (视频教程:http://fishc.co ...

随机推荐

  1. bzoj3839【Pa2013】Działka

    题目描述 平面上有n个不重复的点.每次询问一个边平行坐标轴的矩形内(包含边界)的点组成的凸包的面积.. 输入格式 第一行两个整数k,n(1<=k<=1000000,3<=n<= ...

  2. 【cf859E】Desk Disorder

    Portal --> cf859E Solution ​​ 我们可以将每一个人看成一条边,将位置看成点,然后一个人在新的方案中可以选择的位置就是这条边连接的两个点,然后我们就得到了一个图 ​ 注 ...

  3. 在mvc中 怎么给@Html.HiddenFor()赋值

    @Html.HiddenFor(model => model.CreatedBy, new { @value=currentInfo.UserID}) value始终是null@Html.Tex ...

  4. 树状数组+二分答案查询第k大的数 (团体程序设计天梯赛 L3-002. 堆栈)

    前提是数的范围较小 1 数据范围:O(n) 2 查第k大的数i:log(n)(树状数组查询小于等于i的数目)*log(n)(二分找到i) 3 添加:log(n) (树状数组) 4 删除:log(n) ...

  5. 清除.svn文件(windows & linux)

    如何清除文件夹中的.svn信息 1:来由 当需要在某个svn版本控制下添加某个包时, 常常是在另一个版本控制下sync过来, 但这是这个包是在别的版本控制下, 每个目录下都有版本控制文件.svn, 如 ...

  6. gdb调试4--回退

    加入你正在使用GDB7.0以上版本的调试器并且运行在支持反向调试的平台,你就可以用以下几条命令来调试程序: reverse-continue 反向运行程序知道遇到一个能使程序中断的事件(比如断点,观察 ...

  7. CODEVS 2171 棋盘覆盖

    2171 棋盘覆盖 给出一张nn(n<=100)的国际象棋棋盘,其中被删除了一些点,问可以使用多少12的多米诺骨牌进行掩盖. 错误日志: 直接在模板上调整 \(maxn\) 时没有在相应邻接表数 ...

  8. LintCode 158: Anagram

    LintCode 158: Anagram 题目描述 写出一个函数anagram(s, t)判断两个字符串是否可以通过改变字母的顺序变成一样的字符串. 样例 给出s = "abcd" ...

  9. 对于最近一星期jsp培训有感

    这周上课是jsp的培训,找的是外面的一个培训机构的讲师,人挺好的,讲的也不错,不过交给我们的任务是一星期做出一个网站模型——新闻发布系统. 由于我们组本科学计算机的人实在太少了,所以这个重担落在我的身 ...

  10. CALayer的上动画的暂停和恢复

    CHENYILONG Blog CALayer上动画的暂停和恢复 #pragma mark 暂停CALayer的动画-(void)pauseLayer:(CALayer*)layer{CFTimeIn ...