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. fzyzojP2119 -- 圆圈游戏

    说白了,就是这个样子: 这个玩意明显是一个优美的树形结构 是个森林 然后建个虚点0,并且w[0]=0,然后树形dp即可 f[x]=max(w[x],∑f[son]) 难点是:树怎么建? 就要上计算几何 ...

  2. 团体程序设计天梯赛 L1-009. N个数求和

    易错题 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdboo ...

  3. php 性能优化

    php 性能测试工具 ab(Apache Benchmark) ab 是由 Apache 提供的压力测试软件.安装 apache 服务器时会自带该压测软件. 如何使用: ab -n1000 -c100 ...

  4. Struts2 01---环境搭配

    开发工具:Eclipse   Struts版本:2.3.24  最近在学SSH框架,SSH是 struts+spring+hibernate的一个集成框架,是目前比较流行的一种Web应用程序开源框架. ...

  5. 51nod1019 逆序数

    1019 逆序数 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为 ...

  6. 机器学习算法整理(五)决策树_随机森林——鹃尾花实例 Python实现

    以下均为自己看视频做的笔记,自用,侵删! 还参考了:http://www.ai-start.com/ml2014/ In [8]: %matplotlib inline import pandas a ...

  7. grpc-gateway:grpc转换为http协议对外提供服务

    我所在公司的项目是采用基于Restful的微服务架构,随着微服务之间的沟通越来越频繁,就希望可以做成用rpc来做内部的通讯,对外依然用Restful.于是就想到了google的grpc. 使用grpc ...

  8. Callable,Future,FutureTask

    1.概念定义 2.实现例子 3.总结   1.概念定义   1.Callable Callable是一个接口,效果类似Runnable接口.实现该接口,然后,耗时操作在call()方法中执行.与Run ...

  9. bzoj 2726 [SDOI2012]任务安排(斜率DP+CDQ分治)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2726 [题意] 将n个任务划分成若干个块,每一组Mi任务花费代价(T+sigma{ t ...

  10. Throwable、Error、Exception、RuntimeException 区别

    1.java将所有的错误封装为一个对象,其根本父类为Throwable, Throwable有两个子类:Error和Exception. 2.Error是Throwable 的子类,用于指示合理的应用 ...