通过强制转换 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
}

Live on Coliru

我所做的唯一修改是我使用最终转换为 void* 而不是 size_t,因为我想将地址显示为指针。

我的问题:

  1. 代码是否完全合法,即 "access" 会员通过 nullptr 合法,然后获取其地址?如果是这样的话,那么好像&(((type*)nullptr)->member)计算的是成员相对于0的地址,真的是这样吗? (看起来是这样,因为在最后 3 行中我得到了相对于 s 地址的偏移量)。
  2. 如果我从宏定义中删除对 (void*) 的最终强制转换,则会出现段错误。为什么? &(((type*)nullptr)->member) 不应该是 type* 类型的指针,还是这里的类型被以某种方式删除了?
  1. Is the code perfectly legal?

没有。 It's undefined behavior。编译器可能会选择以这种方式实现 offsetof,但那是因为它 实现:它可以选择如何实现自己的功能。另一方面,你不会得到这样的 "luxury."

您无法实现 offsetof 宏。没有任何 standards-conforming 方式。

  1. 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) 的段错误(因为 xchar 并且该表达式导致 char*),因为 std::ostream's operator<< will try to print char* as a C-style string .