通过转换nullptr获取成员变量的偏移量

vso*_*tco 2 c++ offsetof

我期待在宏观offsetof<cstddef>,看到一个可能的实现是通过

#define my_offsetof(type, member) ((void*) &(((type*)nullptr)->member))
Run Code Online (Sandbox Code Playgroud)

我尝试过它确实按预期工作

#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
}
Run Code Online (Sandbox Code Playgroud)

Live on Coliru

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

我的问题:

  1. 代码是否完全合法,即通过a"访问"成员是合法的nullptr,然后获取其地址?如果是这种情况,那么似乎&(((type*)nullptr)->member)计算成员的地址相对于0,这是否确实如此?(看起来如此,就像在最后3行中我得到相对于地址的偏移量s).
  2. 如果我(void*)从宏定义中删除最终的强制转换,我会得到一个段错误.为什么?不&(((type*)nullptr)->member)应该是类型的指针type*,或者这种类型以某种方式擦除?

Cor*_*lks 6

  1. 代码完全合法吗?

不,这是未定义的行为.编译器可以选择以offsetof这种方式实现,但那是因为它实现:它可以选择如何实现自己的功能.另一方面,你不会得到这样的"奢侈品".

您无法实现offsetof宏.不符合任何标准的方式.

  1. 如果我从宏定义中删除最终的强制转换为(void*),我会得到一个段错误.为什么?不应该&(((类型*)nullptr) - >成员)是类型*的指针,或者这里以某种方式删除的类型?

这可能是从尝试打印段错误my_offsetof(S, x)(因为xchar和表达式的结果char*),因为std::ostreamoperator<<将尝试打印char*作为一个C风格的字符串.