CONTAINING_RECORD 宏

Windows中提供了一个宏

#define CONTAINING_RECORD (address, type, field ) ((type *)( 
                                                  (PCHAR)(address ) - 
                                                  (ULONG_PTR)(&((type *)0)->field)))

  

address : 结构体内成员实际地址
type : 结构体类型
field : 结构体内成员字段名称
 
作用: 更加提供的结构体成员地址反推结构体实例的地址。
 
宏进行的操作实际是:成员变量实际地址 - 成员变量偏移量。
 
大家可能对成员变量偏移量求解比较迷惑,首先假设一个结构体实例地址是从0开始分布的,由于成员变量的地址=结构体实例地址 + 成员变量偏移量,当结构体实例地址为0时,其指向的成员变量的地址就是成员变量的再结构体中的偏移量。
 
这技巧太NB了,不过大家可能还会有个疑惑,这个访问为什么不会出现内存越界问题呢?我刚看到时,第一反应是,为什么没有越界,后来仔细一想,哎,感叹自己对C语言的理解还不够透彻,其实后面计算偏移量时,虽然我们使用了地址0,
 
但是我们并没有存取地址中的任何值。如果我们直接把((type *)0)->field))赋值给另一个变量结构非法内存访问。
 
我们仔细捋下整个操作:
1、((type*)0) 以地址0转换为结构体实例的地址。
2、&(((type*)0)->field) 取字段field的地址,((type*)0)->field这个操作如果我们用它对其他变量赋值则直接出错,否则这只是个空操作什么也没有肯定不会出错,接着我们进行的取地址操作,这个操作相当于只是在结构体的地址基础上增加field变量的偏移量,因此根本不错在访问内存中内容的情况,只是单纯的对地址进行操作,看到这里应该理解了。
3、(ULONG_PTR)(&((type *)0)->field)))最后的转换只是为了字节对齐,使地址足够长。
 
通过汇编,我们看到其实后面求成员变量的偏移量在编译的时候编译器已经算计算好了。
原文地址:https://www.cnblogs.com/ourroad/p/3143385.html