【问题标题】:POD struct (members of same type): are members in contiguous memory locations?POD 结构(相同类型的成员):成员是否位于连续的内存位置?
【发布时间】:2020-01-08 13:03:40
【问题描述】:

给定

template <typename T>
struct Vector3d { T x, y, z; };
  • 假设 x、y 和 z 位于连续的内存位置是否安全?

  • 对于T = floatT = double 至少可以安全假设吗?

  • 如果不能,是否可以跨平台执行?

注意:我不介意在 z 之后填充,只要 x、y、z 是连续的

【问题讨论】:

  • 不,我不这么认为,编译器可以在成员之间(以及最后一个成员之后)添加填充。对于 Tdouble不太可能会有填充,但仍不能保证。
  • 务实的解决方案:static_assert(sizeof(Vector3d) == 3 * sizeof(T));。任何编译器都不太可能违反这一点,因此这在实践中应该可以正常工作,同时仍然可以保护您免受某些病态平台上的微妙错误行为。
  • 你可以改用v.x()
  • 您想将此信息用于什么用途?使用&amp;x[2] 之类的东西访问z 是UB,即使没有填充。

标签: c++ struct memory-alignment


【解决方案1】:

假设 x、y 和 z 位于连续的内存位置是否安全?

语言在技术上没有这样的保证。

另一方面,它们也没有必要不连续,实际上它们很可能是连续的。

如果不能,是否可以跨平台执行?

保证对象位于连续内存位置的跨平台方式是数组:

template <typename T>
struct Vector3d { T components[3]; };

数组也使得使用指针算法迭代对象是合法的。

【讨论】:

    【解决方案2】:

    假设xyz 位于连续的内存位置是否安全?

    标准没有做出这样的保证。

    但实际上,一个理智的实现不会在相同类型的相邻字段之间插入任何填充(因为这种填充从来都不是必需的1)。

    如果您想要额外的安全,请添加static_assert

    static_assert(sizeof(Vector3d<float>) == 3 * sizeof(float));
    

    假设T = floatT = double 至少是安全的吗?

    据我所知,字段类型在这里没有任何区别。


    1 — 保证数组不包含填充。由于您可以创建任何类型的数组,因此实现必须能够将任何单一类型的对象彼此相邻存储而无需填充。

    【讨论】:

      【解决方案3】:

      不,绝对不能保证相同类型的结构元素之间没有填充,即使对于“大”普通旧数据类型,例如 double。此外,通过指针算术对另一个元素的指针尝试到达一个元素的行为是未定义的。

      写得更好

      template <typename T>
      struct Vector3d { T t[3]; };
      

      保证连续性和指针运算,并为xyz提供访问函数。

      如果您不喜欢调用函数的语法,并且愿意容忍struct 本身最有可能表现出来的一些开销,那么您总是可以绑定引用:

      template <typename T>
      struct Vector3d
      {
          T t[3];
          T& x = t[0];
          T& y = t[1];
          T& z = t[2];
      };
      

      【讨论】:

      • 请注意,这些引用无法优化,它们会占用空间。
      • @Sopel:那谁给猴子讲这些? OP 已经表示他们最终可以容忍一些空间。要么就是这样,要么使用函数。
      • 对于 64 位的 Vector3d,它是 28 个字节的“填充”。我只是在警告潜在用户。
      • @Sopel:是的,这是一个明智的警告。我会做出部分回答。
      • 3*4 + 4 填充到 8 字节边界 + 3*8
      【解决方案4】:

      向结构和类添加填充的唯一原因是满足其成员的对齐要求。请注意,结构的对齐是其成员与最大对齐要求的对齐。结构的大小是其对齐方式的倍数,添加尾随填充来满足这一点。

      由于Vector3d 的成员属于同一类型,因此已经满足对齐要求,因此成员之间或结构末尾没有任何填充。

      C++ 标准中没有要求无缘无故插入任意填充。

      为了百分百确定,请输入static_assert

      static_assert(sizeof(Vector3d<float>) == 3 * sizeof(float), "Unexpected layout.");
      static_assert(sizeof(Vector3d<double>) == 3 * sizeof(double), "Unexpected layout.");
      

      有些人说 C++ 标准中没有明确说明保证,因此不能依赖它。然而,C++ 标准通常没有明确规定,因此需要了解其背后的基本原理。在我个人看来,这些人无缘无故地散布恐惧、不确定和怀疑。

      【讨论】:

      • 但是仍然允许一个足够邪恶(或实现不佳)的编译器添加填充。没有保证
      • @TanveerBadar:所有主要编译器都不会为您的示例添加填充。
      • “C++ 标准中没有要求无缘无故插入任意填充。” 也没有要求无缘无故插入填充。
      • @MaximEgorushkin C++ 标准中没有要求 UB 有时会破坏您的程序。然而这种情况发生了(并且标准明确允许,只是不是必需)。
      • @MaximEgorushkin “善于想象不存在的问题” 我们确实是,但是这段话(“C++ 标准中没有要求......” ) 看起来对我有误导性。听起来标准保证没有填充,但事实并非如此。
      猜你喜欢
      • 1970-01-01
      • 2014-04-02
      • 1970-01-01
      • 2017-01-20
      • 1970-01-01
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      相关资源
      最近更新 更多