通过强制转换 nullptr 获取成员变量的偏移量
Getting the offset of a member variable via casting a nullptr
我正在查看 <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
,因为我想将地址显示为指针。
我的问题:
- 代码是否完全合法,即 "access" 会员通过
nullptr
合法,然后获取其地址?如果是这样的话,那么好像&(((type*)nullptr)->member)
计算的是成员相对于0的地址,真的是这样吗? (看起来是这样,因为在最后 3 行中我得到了相对于 s
地址的偏移量)。
- 如果我从宏定义中删除对
(void*)
的最终强制转换,则会出现段错误。为什么? &(((type*)nullptr)->member)
不应该是 type*
类型的指针,还是这里的类型被以某种方式删除了?
- Is the code perfectly legal?
没有。 It's undefined behavior。编译器可能会选择以这种方式实现 offsetof
,但那是因为它 是 实现:它可以选择如何实现自己的功能。另一方面,你不会得到这样的 "luxury."
您无法实现 offsetof
宏。没有任何 standards-conforming 方式。
- If I remove the final cast to (void*) from the macro definition, I get a segfault. Why? Shouldn't &(((type*)nullptr)->member) be a pointer of type type*, or is the type somehow erased here?
这可能是尝试打印 my_offsetof(S, x)
的段错误(因为 x
是 char
并且该表达式导致 char*
),因为 std::ostream
's operator<<
will try to print char*
as a C-style string .
我正在查看 <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
,因为我想将地址显示为指针。
我的问题:
- 代码是否完全合法,即 "access" 会员通过
nullptr
合法,然后获取其地址?如果是这样的话,那么好像&(((type*)nullptr)->member)
计算的是成员相对于0的地址,真的是这样吗? (看起来是这样,因为在最后 3 行中我得到了相对于s
地址的偏移量)。 - 如果我从宏定义中删除对
(void*)
的最终强制转换,则会出现段错误。为什么?&(((type*)nullptr)->member)
不应该是type*
类型的指针,还是这里的类型被以某种方式删除了?
- Is the code perfectly legal?
没有。 It's undefined behavior。编译器可能会选择以这种方式实现 offsetof
,但那是因为它 是 实现:它可以选择如何实现自己的功能。另一方面,你不会得到这样的 "luxury."
您无法实现 offsetof
宏。没有任何 standards-conforming 方式。
- If I remove the final cast to (void*) from the macro definition, I get a segfault. Why? Shouldn't &(((type*)nullptr)->member) be a pointer of type type*, or is the type somehow erased here?
这可能是尝试打印 my_offsetof(S, x)
的段错误(因为 x
是 char
并且该表达式导致 char*
),因为 std::ostream
's operator<<
will try to print char*
as a C-style string .