由结构体成员地址计算结构体地址——list_entry()原理详解

  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. const typeof(((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_t size_t;
  2. typedef unsigned int __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. const typeof(((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类型的指针。

再向上就是一些宏定义没有什么可说的了。

来自为知笔记(Wiz)

时间: 2024-10-28 14:18:32

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

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

一般硬盘正面贴有产品标签,主要包括厂家信息和产品信息,如商标.型号.序列号.生产日期.容量.参数和主从设置方法等.这些信息是正确使用硬盘的基本依据,下面将逐步介绍它们的含义. 硬盘主要由盘体.控制电路板和接口部件等组成,如图1-1所示.盘体是一个密封的腔体.硬盘的内部结构通常是指盘体的内部结构:控制电路板上主要有硬盘BIOS.硬盘缓存(即CACHE)和主控制芯片等单元,如图1-2所示:硬盘接口包括电源插座.数据接口和主.从跳线,如图1-3所示. 图1-1 硬盘的外观 图1-2 控制电路板 图1-

.Net的Oracle数据库ORM控件dotConnect for Oracle下载地址及功能详解

原文来自龙博方案网http://www.fanganwang.com/product/1330转载请注明出处 dotConnect for Oracle完全基于ADO.NET方法,因此您完全可以采用标准ADO.NET数据提供的方法来使用它.是一款为Microsoft .NET Framework提供直接Oracle数据库连接的数据发生器控件. 具体功能: 无需Oracle客户端,采用直接模式提供数据库连接 100%代码管理 具有高表现性能 支持Oracle 10g, 9i, 8i 和 8.0,包

硬盘内部硬件结构和工作原理详解

一般硬盘正面贴有产品标签,主要包括厂家信息和产品信息,如商标.型号.序列号.生产日期.容量.参数和主从设置方法等.这些信息是正确使用硬盘的基本依据,下面将逐步介绍它们的含义. 硬盘主要由盘体.控制电路板和接口部件等组成,如图1-1所示.盘体是一个密封的腔体.硬盘的内部结构通常是指盘体的内部结构:控制电路板上主要有硬盘BIOS.硬盘缓存(即CACHE)和主控制芯片等单元,如图1-2所示:硬盘接口包括电源插座.数据接口和主.从跳线,如图1-3所示. 图1-1 硬盘的外观 图1-2 控制电路板 图1-

PE详解之IMAGE_DOS_HEADER结构定义即各个属性的作用(PE详解01)

小甲鱼在这里为大家做好了详细的注释,免得大家一头雾水,另外可以结合小甲鱼<加密系列>-系统篇-PE结构详解视频教程学习~若有纰漏之处还望大家不吝指正. (视频教程:http://fishc.com/a/shipin/jiemixilie/) (注:最左边是文件头的偏移量.) IMAGE_DOS_HEADER STRUCT { +0h WORD e_magic   // Magic DOS signature MZ(4Dh 5Ah)     DOS可执行文件标记 +2h   WORD  e_cb

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

PE Header 是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,里边包含着许多PE装载器用到的重要字段.下边小甲鱼将为大家详细讲解哈~ (视频教程:http://fishc.com/a/shipin/jiemixilie/) 首先是IMAGE_NT_HEADERS 结构的定义:(啥?结构不会,先看看小甲鱼童鞋的<零基础入门学习C语言>关于结构方面的章节吧~) IMAGE_NT_HEADERS STRUCT { +0hDWORDSignature  // +4h  IMAG

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

首先是IMAGE_NT_HEADERS 结构的定义:(啥?结构不会,先看看小甲鱼童鞋的<零基础入门学习C语言>关于结构方面的章节吧~) IMAGE_NT_HEADERS STRUCT { +0hDWORDSignature  // +4h  IMAGE_FILE_HEADER FileHeader // +18hIMAGE_OPTIONAL_HEADER32OptionalHeader   // } IMAGE_NT_HEADERS ENDS Signature 字段: 在一个有效的 PE 文

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

咱接着往下讲解IMAGE_OPTIONAL_HEADER32 结构定义即各个属性的作用! (视频教程:http://fishc.com/a/shipin/jiemixilie/) 接着我们来谈谈 IMAGE_OPTIONAL_HEADER 结构,正如名字的意思,这是一个可选映像头,是一个可选的结构,但是呢,实际上上节课我们讲解的 IMAGE_FILE_HEADER 结构远远不足以来定义 PE 文件的属性.因此,这些属性在 IMAGE_OPTIONAL_HEADER 结构中进行定义. 因此这两个结

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

咱接着往下讲解IMAGE_OPTIONAL_HEADER32 结构定义即各个属性的作用! (视频教程:http://fishc.com/a/shipin/jiemixilie/) 接着我们来谈谈 IMAGE_OPTIONAL_HEADER 结构,正如名字的意思,这是一个可选映像头,是一个可选的结构,但是呢,实际上上节课我们讲解的 IMAGE_FILE_HEADER 结构远远不足以来定义 PE 文件的属性.因此,这些属性在 IMAGE_OPTIONAL_HEADER 结构中进行定义. 因此这两个结

小甲鱼PE详解之IMAGE_DOS_HEADER结构定义即各个属性的作用(PE详解01)

(注:最左边是文件头的偏移量.) IMAGE_DOS_HEADER STRUCT { +0h  WORD  e_magic  // Magic DOS signature MZ(4Dh 5Ah)     DOS可执行文件标记 +2h  WORD  e_cblp //Bytes on last page of file +4h  WORD  e_cp  //Pages in file +6h  WORD  e_crlc  //Relocations +8h  WORD  e_cparhdr   /