我对CONTAINING_RECORD宏的详细解释

Windows 内核编程:使用双向链表处理数据结构 首先,我们需要定义一个结构,包含数据成员和链表节点。在这个示例中,我们定义一个名为的结构体,其中包含一个整型数据成员m_Data和一个LIST_ENTRY类型的链表节点。 阅读详情

转载出处:http://www.cnblogs.com/nbsofer/archive/2013/01/07/2849913.html

宏CONTAINING_RECORD的用处其实还是相当大的, 而且很是方便, 它的主要作用是:
    根据结构体中的某成员的地址来推算出该结构体整体的地址!
  下面从一个简单的例子开始说起:
  我们定义一个结构体, 同时类型化:

typedef struct{
    int a;
    int b;
    int c;
}ss;

  这是一个很简单的结构体, 没什么特殊的, 稍微分析下该结构体(假设在32位平台上):
    结构体的大小(字节):4+4+4=12字节
    成员a的偏移:0
    成员b的偏移:4
    成员c的偏移:8
  我们用ss来定义一个变量:
    ss s = {1,2,3};
  那么此时a,b,c的值分别为:a=1,b=2,c=3.
其实编译器在生成代码的时候其实是这样给成员变量赋值的:
  假定s的地址为:0x12000000, 则:
    *(int*)((char*)&s + 0) = 1;
    *(int*)((char*)&s + 4) = 2;
    *(int*)((char*)&s + 8) = 3;
  也就是说是在&s的地址基础上加上变量的偏移来确定成员的指针并赋值的, 所以:
    &s->a 将得到 0x12000000 + 0 = 0x12000000
    &s->b 将得到 0x12000000 + 4 = 0x12000004
    &s->c 将得到 0x12000000 + 8 = 0x12000008
  所以:    结构体的地址   +  成员变量的偏移 = 成员变量的地址
  
移一下项:  成员变量的地址 -  成员变量的偏移 = 结构体的地址
  哇哇, 这就是我们想要的地址, 不就是做了个减法嘛~~~囧

  首先, 成员变量的地址是我们知道的. 
  其次, 我们需要得到成员变量的偏移(假定为成员b的偏移).
    怎么办呢? 我们可以这样:
      &s->b - (unsigned long)&s, 这样就可以得到成员b的偏移了, 但是, 但是, &s 是我们需要的, 显然暂时是个未知数, 既然这样...
    那, 我们再做一次减法吧(非正确的C语言表达式, 不过结果没问题, 这里只是显得清楚点):
      &(s-s)->b - (unsinged long)&(s-s), 
    其中, 为保证类型一致, 需要:
      s-s = (ss*)0
      (unsigned long)&(s-s) = (unsigned long)(ss*)0 = 0, 直接省略该部分就可以了
    那么, 化简得到: &((ss*)0)->b - (unsigned long)0
    最简结果: &((ss*)0)->b, 这就是b的偏移
  哈哈, 很简单吧, 0指针的妙用, 总共做了两次减法而已~ 对你们数学帝来说肯定不是问题啦~
  其中, 我们需要知道ss结构体的原型, ss结构体中的某个成员变量b(其实无论哪个都一样, 只是要和前面提供指针的那个变量要一致)

  总结下, 我们需要提供:结构体中某个成员变量的地址, 该结构体的原型, 该结构体中的某个成员变量(与前面要是同一个变量)

  最终的CONTAINING_RECORD的定义为:

#define CONTAINING_RECORD(addr,type,field) ((type*)((unsigned char*)addr - (unsigned long)&((type*)0)->field))
  addr:  结构体中某个成员变量的地址
  type:  结构体的原型
  field: 结构体的某个成员(与前面相同)


  好了, 所有的结论都出来了, 这是一个万能公式, 不管成员变量是哪一个结果都正确, 这是相对于知道第一个变量的地址而言的:
    如果知道的是第一个成员的地址(pa = &s->a)的话, 这是最简单的情况了:
    直接强制类型转换就可以了: (ss*)pa 即可, 此时 &((type*)0)->field 这部分恰好为0
    所以结果直接就是((type*)addr)了, 最简单的情况. 也是我们最容易想到的一种情况, 比如把链表元素放在结构体的最开始 ~~~
  
  到这里这个CONTAINING_RECORD宏就已经说完了~  
  现在, 我们在使用LIST_ENTRY等双向链表时, 不管把该链表放在结构体的哪个地方, 都可以在遍历链表时通过CONTAINING_RECORD宏来准确得到整个结构体的地址了~
记得移除链表中的某个元素的时候, 要free整个结构体的地址才行哦, WDK提供的操作函数只是把该链表元素脱离整个链表~~~

btw:
  把addr转换为 unsigned char*的原因是在指针计算时的计算单位为1, 也就是说 (unsigned char*)addr+1 = addr+1, 不转换的话肯定是错误的
  把&((type*)0)->field转换为(unsigned long)4个字节宽的同时是要保证表达式不是由两个指针的算术操作构成的, 因为C语言标准未定义那样的运算

C++ CONTAINING_RECORD 宏操作技巧学习 C++ 的CONTAINING_RECORD宏; 阅读详情

相关推荐

CentOS7全隔离环境部署Nvidia Tesla A100 40G驱动与CUDA工具包实战

本文详细介绍了在CentOS7全隔离环境下部署Nvidia Tesla A100 40G显卡驱动与CUDA工具包的实战经验。通过构建本地YUM仓库、解决驱动依赖问题以及三重验证法,确保Tesla A100与CUDA 11.7的完美兼容。同时分享了Anaconda3离线部署技巧,帮助用户在隔离环境中高效搭建Python GPU开发环境。

weixin_27215337的博客 399

CONTAINING_RECORD宏原理与使用详解

先不急着说CONTAINING_RECORD宏,我们从最浅显的代码开始讲解。 一、0指针的妙用 0指针,即nullptr、NULL,空指针,是不是很常见,一遇到它往往就是segment fault。代码,如下: struct Test { int a; float b; }; Test* pTest = nullptr; int a_value = pTest->a; // segment fault int b_value = pTest->b; // segment fau

百里杨的博客 3413

极化码自适应CA-SCL译码(极化码译码)

我们知道最初极化码提出来是运用SC译码,但短码和中等长度其译码性(误码率)能不如LDPC码码,,为了提升有限长度极化码的译码性能,我们主要从译码器的性能和极化码的纠错性能来考虑。 SCL:在增加复杂度的情况下,大幅度增加极化码纠错性能,在高信噪比下,与最优ML性能相同(L=32) CA-SCL:改善最小汉明距离(最小汉明距离增大),增加其纠错性能。把CRC和极化码极联。 自适应CA-SCL:CA-SCL译码虽然提升几乎没的纠错性能,但是导致复杂度大幅度增加,为了降低其复杂度,让L自适应,从而降低其复杂度

m0_52610504的博客 3294

CONTAINING_RECORD宏解析

CONTAINING_RECORD内容 这里分析一下这个CONTAINING_RECORD宏,该宏的作用是通过结构体类型内部某一成员的位置来获取该结构体的位置。结构体内容如下: // // Calculate the address of the base of the structure given its type, and an // address of a field within the structure. // #define CONTAINING_RECORD(address, t

輚至消亡 1161

Python | 集合(set)运算之intersection()

简介 在做力扣题的时候,有一个思路是要求解两个字符串的交集。 一般来说,求解交集的思路是遍历两个字符串,判断有无相等,有相等则添加到交集中,直到循环结束。 但是这样做,会增加很多的不必要的遍历:比如说,当一个字符串中有多个相同的元素时,该元素会被遍历多次。 因此,在求解时,我们可以先将字符串转换为set集合类型。集合中存储的元素没有顺序,且互异(即元素不可重复)。 算例 求字符串m“abcdaaa” 与 字符串n“bcddses”是否存在交集 首先将字符串转换为set类型: m = set(m) n

lovetaozibaby的博客 1万+

MINI PCIE接口设计要点:

3.3Vaux(5pins):3.3V电源。PERST#信号通过外接复位电路,可实现模块复位。模块可以通过硬件方式或软件方式关闭射频通信(不包括 GNSS)。LED_WWAN#:(O)漏极开漏,低有效。:(I)低激活系统禁止射频信号(飞行模式),因此需加上拉。UIM_PWR (1 pin):(O)UIM的电源输出。SMB_DATA:SMBus2.0数据信号线。UIM_VPP:(O)A类设备的可变电源电压。SMB_CLK:SMBus2.0时钟信号线。UIM_RESET:(O)UIM的复位信号。

qq_40010777的博客 8238

CONTAINING_RECORD宏的详细解释

我对CONTAINING_RECORD宏的详细解释 标签: C语言, 结构体 日期: 2013-01-07 分类: C&C++ 字号:  宏 CONTAINING_RECORD 的用处其实还是相当大的, 而且很是方便, 它的主要作用是:根据结构体中的某成员的地址来推算出该结构体整体的地址! 下面从一个简单的例子开始说起, 我们定义一个结构体, 同时类型化: typedef stru

zxy355的专栏 454

DLMS-Blue-Book蓝皮书—曲线类

attribute_index 1 指的是第 1 个属性(即 logical_name),attribute_index 2 指的是第 2 个属性,等等;如果其他“曲线通用类”对象捕获了这个缓冲区,它们的缓冲区也将被清空,以保证它们的缓冲区条目是同质的。如果捕获对象是曲线通用类的缓冲区,则 data_index 标识缓冲区(即列)中的捕获对象(即内部曲线通用类中的列);如果曲线通用类已排序,则调用捕获方法将把新条目存储在缓冲区中的适当位置,移动所有后续条目,并可能丢失最不相关的条目。

一个bug是bug,多个bug能work 1452

CONTAINING_RECORD宏的详细解释(转)

宏CONTAINING_RECORD的用处其实还是相当大的, 而且很是方便, 它的主要作用是:     根据结构体中的某成员的指针来推算出该结构体的指针!   下面从一个简单的例子开始说起:   我们定义一个结构体, 同时类型化: typedef struct{ int a; int b; int c; }ss;   这是一个很简单的结构体, 没什么特殊的

熊晓杰 -- (暴熊 || kongbu0621 || kongbu0622) 的专栏 731

系规备考论文:论IT服务规划设计

系规备考论文:论IT服务规划设计

liuxu324的博客 1144

授时中心被攻击?Windows 11 电脑时间不准?手把手教您如何更换国内稳定高速的时间服务器(时间服务器整理)

近期授时中心被攻击,时间同步重要性凸显。Windows 11 默认服务器在国内常遇网络问题。本文教你轻松修改时间服务器,选择更稳定、快速的国内优质服务器,彻底解决电脑时间不准的烦恼,让你的系统时间永远精准如一!🚀

XiaoqiangClub的博客 1082

C++Windows下CONTAINING_RECORD宏的用法和详解

#include #include int main() { struct ABCD { int a; int b; int c; int d; }; //CONTAINING_RECORD宏的作用就是根据结构体 //类型和结构体中成员变量地址和名称则可求出 //该变量所在结构体的指针 ABCD Abcd = { 1, 2, 3, 4 }; //假设我们知道AB

F_r_e_e_x的专栏 5533

Transformer从菜鸟到新手(一)

从本文开始回顾一下Transformer的原理与实现细节,包括分词算法BPE的实现。最终利用从零实现的Transformer模型进行英中翻译。

日积月累,天道酬勤 2124

CONTAINING_RECORD宏的实现原理

在使用windows完成端口时,会使用到CONTAINING_RECORD宏,该宏的作用就是:根据结构体中的某成员的地址来推算出该结构体整体的地址,相当于一个万能公式。 下面代码的注释中,讲解了该宏的实现原理。 #include #include int main() { struct T { int a; int b;

while(1) { smile(); } 9万+

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

CONTAINING_RECORD 宏

CONTAINING_RECORD 这样的一个宏,我看了它的定义,如下:#define CONTAINING_RECORD(address, type, field) ((type *)( (PCHAR)(address) - (ULONG_PTR)(&((type*)0)->field)))class A{ char c; int a; s...

dingbianping4275的博客 186

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

CONTAINING_RECORD宏

<br />#define CONTAINING_RECORD(address, type, field) ((type *)( (PCHAR)(address) - (ULONG_PTR)(&((type*)0)->field)))<br />class A<br />{<br />      char c;<br />      int a;<br />      short b;<br />}<br />int a = 100;<br />int *pInt = &a;<br />比如,我调用了 CO

jian_275的专栏 1400

CONTAINING_RECORD _CR 宏理解

在EFI中常常遇到下面这个宏,他的作用是  // //  CONTAINING_RECORD - returns a pointer to the structure //      from one of it's elements. // #define _CR(Record, TYPE, Field)  ((TYPE *) ((CHAR8 *) (Record) - (CHAR

bailin2010的专栏 1027
上一篇: C/C++ windows 获取CPU核数
下一篇: 数据库表分区简单备注
pingd
博客等级 码龄15年 60粉丝 54原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值