我遇到了以下c ++代码:
#define OFFSETOF_MEMBER(t, f) \
(reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u)) // NOLINT
其中 t 是类型,f 是字段名称。我想知道为什么我们可以将整数 16 作为 reinterpret_cast 的参数。
我遇到了以下c ++代码:
#define OFFSETOF_MEMBER(t, f) \
(reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u)) // NOLINT
其中 t 是类型,f 是字段名称。我想知道为什么我们可以将整数 16 作为 reinterpret_cast 的参数。
16 是我们分配给指针的地址,它可以让我们计算指定成员的偏移量。指针的地址只是一个数字,因此我们可以滥用这一事实来获取有关我们的结构/类的信息。
假设我们有一个结构:
struct point {
//Assuming 32-bit integer sizes.
//For 64-bit integersizes, 0x0, 0x8, 0x10 for the integer offsets
int x; //Offset 0x0
int y; //Offset 0x4
int z; //Offset 0x8
}; static_assert(sizeof(point) == 12 /* or 0xC in hex */);
我们使用宏:
OFFSETOF_MEMBER(point, y);
展开宏,我们得到:
(reinterpret_cast<uintptr_t>(&reinterpret_cast<point*>(16)->y) - static_cast<uintptr_t>(16u)
另一种表达方式reinterpret_cast<point*>(16)->y
可能是这样的:point * myPt = 16u;
我们知道 16 不是有效地址,但编译器没有,只要我们不尝试读取我们指向的地址,我们就可以.
接下来,我们可以将所有内容简化&reinterpret_cast<point*>(16)->y
为:&myPt->y
。我们从上面知道 y 是@offset 0x4,并且由于 myPt 是 16:16 + 0x4 = 20
然后我们有reinterpret_cast<uintptr_t>(20u) - static_cast<uintptr_t(16u)
or 20 - 16
,它给出了 y 的偏移量,即 0x4。
整数 16 只是一个内存地址。表达式reinterpret_cast<t*>(16)
只是意味着“将地址 16 处的对象解释为类型t
”,但您知道该地址处没有这样的t
对象。理论上,16
可以替换为任何 4x(32 位)或 8x(64 位)整数。如果选择0
,宏可以简化为:
#define OFFSETOF_MEMBER(t, f) \
(reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(0)->f))
有关更多信息,请参见offsetof。