我正在查看<cstddef>
的宏offsetof
,并看到可能的实现是通过
#define my_offsetof(type, member) ((void*) &(((type*)nullptr)->member))
我试过了,确实它按预期工作
#include <iostream>
#define my_offsetof(type, member) ((void*) &(((type*)nullptr)->member))
struct S
{
char x;
short y;
int z;
};
int main()
{
std::cout << my_offsetof(S, x) << 'n';
std::cout << my_offsetof(S, y) << 'n';
std::cout << my_offsetof(S, z) << 'n';
S s;
std::cout << (void*) &((&s)->x) << 'n'; // no more relative offsets
std::cout << (void*) &((&s)->y) << 'n'; // no more relative offsets
std::cout << (void*) &((&s)->z) << 'n'; // no more relative offsets
}
住在科里鲁
我所做的唯一修改是我使用最终强制转换来void*
而不是size_t
,因为我想将地址显示为指针。
我的问题:
- 代码是否完全合法,即通过
nullptr
"访问"成员,然后获取其地址是否合法?如果是这样的话,那么似乎&(((type*)nullptr)->member)
计算成员相对于 0 的地址,事实确实如此吗?(似乎是这样,因为在最后 3 行中,我得到了相对于s
地址的偏移量)。 - 如果我从宏定义中删除要
(void*)
的最终转换,则会出现段错误。为什么?&(((type*)nullptr)->member)
不应该是类型type*
的指针,还是该类型在此处以某种方式被删除?
- 代码完全合法吗?
不。这是未定义的行为。编译器可以选择以这种方式实现offsetof
,但那是因为它是实现:它可以选择如何实现自己的功能。另一方面,你没有得到这样的"奢侈品"。
您无法实现offsetof
宏。不符合任何标准。
- 如果我从宏定义中删除最终转换为 (void*),我会得到一个段错误。为什么?&((type*)nullptr)->member) 不应该是类型为 * 的指针,还是此处以某种方式删除了该类型?
这可能是尝试打印my_offsetof(S, x)
的段错误(因为x
是一个char
并且该表达式会导致char*
),因为std::ostream
的operator<<
将尝试将char*
打印为C样式字符串。