【问题标题】:Getting the offset of a member variable via casting a nullptr通过强制转换 nullptr 获取成员变量的偏移量
【发布时间】:2016-01-27 03:38:44
【问题描述】:

我正在查看来自<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. 代码是否完全合法,即通过nullptr“访问”成员,然后获取其地址是否合法?如果是这样的话,那么&amp;(((type*)nullptr)-&gt;member)似乎计算了成员相对于0的地址,真的是这样吗? (看起来是这样,在最后 3 行中,我得到了相对于s 地址的偏移量)。
  2. 如果我从宏定义中删除最终转换为(void*),我会得到一个段错误。为什么? &amp;(((type*)nullptr)-&gt;member) 不应该是 type* 类型的指针,还是该类型在这里被抹掉了?

【问题讨论】:

  • 取消引用空指针是 UB,即使它只是获取它的地址。
  • @JonathanPotter 这就是我的想法,虽然不确定。所以cppreference“可能的实现”被打破了。然后你将如何实现这个宏?
  • @vsoftco:并非 C++ 标准中指定的所有内容都可以由用户重新实现。某些事物,例如 offsetof 或 std::complex(例如),无法以符合标准的方式实现。
  • @Cornstalks 我明白了...std::complex 有什么问题?不就是一个 struct 有 2 个成员和一些成员函数吗?
  • @vsoftco:保证没有任何填充,并且可以转换为float[2](或double[2])数组。 Unfortunately, there is no mechanism in the C++ standard that lets users provide that level of guarantee with their own classes.

标签: c++ offsetof


【解决方案1】:
  1. 代码完全合法吗?

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

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

  1. 如果我从宏定义中删除最终强制转换为 (void*),则会出现段错误。为什么? &(((type*)nullptr)->member) 不应该是 type* 类型的指针,还是该类型在这里被抹掉了?

尝试打印 my_offsetof(S, x) 可能是段错误(因为 x 是 char 并且该表达式导致 char*),因为 std::ostream's operator&lt;&lt; will try to print char* as a C-style string。

【讨论】:

  • 不确定我是否理解“编译器可能会选择以这种方式实现 offsetof,但那是因为它是实现。”您的意思是在这种情况下,编译器在内部具有定义明确的行为?
  • @vsoftco:我的意思是编译器需要提供一些offsetof 宏。但是,如何 由编译器决定。如果他们选择这样做,他们可以为他们的offsetof 宏定义好&amp;(((type*)nullptr)-&gt;member)。但这并不能很好地为您自己的宏定义。
  • @Cornstalks,在你的最后一句话中 s/probably// :)
  • 谢谢,这也是我从您的回答中了解到的。哦,我也看到char 会发生什么......由于某些原因,我认为类型是S*,但类型是指向成员的指针,所以只要成员是char,它就会中断,因为调用 char* 重载。 +1
  • @vsoftco 您正在做的事情可能不适用于所有编译器。但它可能适用于 GCC 5。如果它适用于 GCC 而不是 MSVC,那么 MSVC 将提供他们自己的不同的宏并且确实适用于 MSVC。如果您使用自己的版本,那么您将不知道为 MSVC 更改它,并且您的代码将无法在 MSVC 上运行。
猜你喜欢
  • 2011-01-03
  • 1970-01-01
  • 1970-01-01
  • 2021-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-27
  • 2014-07-22
相关资源
最近更新 更多