C 中的结构偏移和指针安全



这个问题是关于使用指针算术与struct Offsets得出的指针。

考虑以下程序:

#include <cstddef>
#include <iostream>
#include <new>
struct A {
  float a;
  double b;
  int c;
};
static constexpr auto off_c = offsetof(A, c);
int main() {
  A * a = new A{0.0f, 0.0, 5};
  char * a_storage = reinterpret_cast<char *>(a);
  int * c = reinterpret_cast<int *>(a_storage + off_c));
  std::cout << *c << std::endl;
  delete a;
}

此程序似乎有效,并使用默认设置和C 11标准对我测试的编译器给出了预期结果。

(一个密切相关的程序,我们使用void *而不是char *static_cast而不是reinterpret_cast,不是普遍接受的。gcc 5.4发出有关用无效指针的指针算术的警告,clang 6.0说指针使用void *的算术是一个错误。)

该程序根据C 标准具有明确定义的行为?

答案是否取决于实现是放松还是严格的指针安全([basic.stc.dynamic.safety])?

您的代码中没有基本错误。

如果A不是普通的旧数据,则以上是UB(在C 17之前),并有条件地支持(C 17之后)。

您可能需要用auto*替换char*int*,但这是一种样式的东西。

请注意,对成员的指针以类型安全的方式进行了完全相同的事情。大多数编译器将指向成员的指针作为类型中成员的偏移。但是,他们确实在任何地方都可以在任何地方工作。

旁边:我看不出offsetof是标准中的constexpr。;)

无论如何,替换:

static constexpr auto off_c = offsetof(A, c);

static constexpr auto off_c = &A::c;

  auto* a_storage = static_cast<char *>(a);
  auto* c = reinterpret_cast<int *>(a_storage + off_c));

  auto* c = &(a->*off_c);

以C 方式进行。

它在您的特定示例中是安全的,但这仅仅是因为您的struct是标准布局,您可以使用std::is_standard_layout<>进行仔细检查。

试图将其应用于:

之类的结构
struct A {
  float a;
  double b;
  int c;
  std::string str;
};

即使字符串已经超过结构的一部分,

也是非法的。

编辑

引起的内容:在3.7.4.3中[BASIC.STC.DYNAMIC.SAFETY]它说,仅当(条件)和我们具有严格的指针安全性时,指针是安全得出的这样的地方。在5.7指针算术中,它说我可以在数组中进行平常的算术,但是我看不到任何东西告诉我struct offsect oftsotiget antmecety是可以的。我试图弄清楚这是否与我认为的方式无关,或者在假设的"严格"中,构造偏移算术是否不正确,或者我读过5.7错误(N4296)

当您执行指针算术时,您将在char的数组中执行它,至少sizeof(A)的大小,所以很好。

然后,当您归还第二个成员时,您将被(2.4)覆盖:

- 安全衍生的指针值明确定义的指针转换(4.10,5.4)的结果;

您应该检查您的假设。

假设#1)Offsetof给出了字节中正确的偏移。仅当该类被视为具有许多限制(例如没有虚拟方法)的"标准路线"时,才可以保证这一点。确保。

假设#2)char与字节的大小相同。在C中,根据定义,这是安全的。

假设#3)偏移给出了从指针到类的正确偏移,而不是从数据开头。这基本上与#1相同,但是VTable肯定是一个问题。同样,仅与标准层一起使用。

相关内容

  • 没有找到相关文章

最新更新