如何通过结构体成员地址获取父地址

有如下结构体:

1
2
3
4
5
6
7
8
struct Test {
int a;
// ....
// 还有若干成员
// ...
int y;
int z;
};

在只知道成员变量 y 地址的情况下,如何获取到结构体的首地址?

可以通过如下宏来实现:

1
2
3
#define GET_PARENT_ADDRESS(address, type, field) ((type *)( \
(char*)(address) - \
(ULONG_PTR)(&((type *)0)->field)))

使用方法如下:

1
2
3
4
5
6
7
8
9
int main()
{
Test t = { 1, 2, 3 };

Test* pT = GET_PARENT_ADDRESS(&t.b, Test, b);
printf("a:%d b:%d c:%d\n", pT->a, pT->b, pT->c); // 输出a: 1 b:2 c:3

return 0;
}

核心原理:

通过将空指针强转成 Test* 类型, 然后再取成员变量 b 的地址,该操作的作用是:假设 Test 开始在 0x00000000 内存位置上分配内存,并在此基础上得 b 的内存地址,这样等同于获得了成员 b 的结构体对齐后偏移量。

最后,用 b 的实际内存地址减去 b 的偏移量就是结构体的首地址。

参考:Microsoft 完成端口 CONTAINING_RECORD 宏的实现。

本文最初发布在我的 CSDN 博客:《CONTAINING_RECORD 宏的实现原理》,在此基础上稍作整理。