【总结】【UEFI】CONTAINING_RECORD _CR 宏理解

转自:http://blog.csdn.net/bailin2010/article/details/6955440

在EFI中常常遇到下面这个宏,他的作用是 

//
//  CONTAINING_RECORD - returns a pointer to the structure
//      from one of it's elements.
//


#define _CR(Record, TYPE, Field)  ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))

这个宏的作用是根据一个结构体成员变量的的地址获得该结构体基地址:

分析~~~

宏前面一段((TYPE *) ((CHAR8 *) (Record)表示成员b的当前地址。

先看关键的后面这段 (CHAR8 *) &(((TYPE *) 0)->Field))),表示成员变量B距离结构体首地址的偏移量offset

ANSI C标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此((TYPE *)0)的结果就是一个类型为TYPE *的NULL指针,如果利用这个NULL指针来访问TYPE 的成员当然是非法的,但&(((TYPE *)0)->Field)的意图并非想存取Field字段内容,而仅仅是计算当结构体实例的首址为((TYPE *)0)时TYPE 字段的地址。编译器根本就不生成访问Field的代码,而仅仅是根据TYPE类型Struct的内存布局和结构体实例首址在编译期计算这个(常量)地址,这样就完全避免了通过NULL指针访问内存的问题。又因为首址的值为0,所以这个地址的值就是字段相对于结构体基址的偏移。“&”取地址符号在这里取的是->Field 成员的地址,而他的地址肯定是相对于结构体开始位置的偏移量了。

我们来分析一个具体的例子:

typedef struct {
UINT16 A;

UINT16 B;

UINT32 C;

} DATA;

DATA  Private_data ={a,b,c};

图示如下:


已知道的条件是成员b的地址实际地址add1,如何得到Private_data 的地址add0呢?

我们知道数据在内存中是按照小地址往大地址里面纯的 所以 add0=add1-成员b相对于结构体首地址的偏移量offset。

代入已知变量到宏里面去

#define _CR(Record, TYPE, Field)  ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))

add0 = _CR(add1, struct DATA  , B)  ((TYPE *) ((CHAR8 *) (add1) - (CHAR8 *) &(((struct DATA  *) 0)->B)))

猜你喜欢

转载自blog.csdn.net/antchen88/article/details/79655053
CR
今日推荐