CONTAINING_RECORD
Containing record是一個在C++編程中用處很大的一種技巧,它的功能為已知結構體或類的某一成員、對象中該成員的地址以及這一結構體名或類名,從而得到該對象的基地址。
由於寫法簡單,它被當做一個宏來使用,寫法是這樣的。
#define CONTAINING_RECORD(address,type,field) \
((type*)((PCHAR)(address)-(ULONG_PTR)(&((type*)0)->field)))
這個宏運用的是地址的偏移來實現的,這里我們需要補充一個知識點:
用type*來對0進行強制類型轉換(type是一個結構體名或類名)再調用其成員后,該成員的地址就等於其相對於基地址的相對地址即偏移。
typedef struct { //相對地址
int m_1; //0x0
int m_2; //0x4
int m_3; //0x8
}DATA,*PDATA;
int main()
{
int* v3 = (int*)(&((PDATA)0)->m_2);
printf("%p\r\n",v3);
//最后輸出結果為4,即m_2的相對地址。
}
明白了這個就更容易理解CONTAINING_RECORD的實現了,顯然,該對象的絕對地址,就等於對象中某一個成員的絕對地址減去這一成員的相對地址。而剛剛我們計算的就是這一相對地址。(ULONG_PTR)(&((type*)0)->field))
(PCHAR)(address) 則是該成員的絕對地址,最后兩個相減,就得到了對象的基地址,利用這個基地址可以做很多事情。
注意取地址之后的強制類型轉換
最終的實現:
typedef struct { //相對地址
int m_1; //0x0
int m_2; //0x4
int m_3; //0x8
}DATA,*PDATA;
// 利用地址的偏移和對0用結構體指針型強制類型轉換來實現對結構體基地址的尋址。
//三參數:某一成員、對象中該成員的地址以及這一結構體名或類名
#define CONTAINING_RECORD(address,type,field) ((type*)((PCHAR)(address)-(ULONG_PTR)(&((type*)0)->field)))
int main()
{
DATA Data = {33,22,11};
//int* v3 = (int*)(&((PDATA)0)->m_1);
int* v1 = &Data.m_2;
PDATA v2 = CONTAINING_RECORD(v1, DATA, m_2);
printf("%d %d %d\n", v2->m_1,v2->m_2,v2->m_3);
//printf("%p\r\n", v3);
return 0;
}
//最終v2就是對象Data的地址,我們可以不使用Data就直接訪問該對象的成員
