将数组重新解释为包含数组的结构是否安全?

时间:2014-12-23 07:36:54

标签: c++ struct alignment

如果我有一个数组或一个指向数组的指针,是否可以安全地将它重新解释为包含数组的结构,关于对齐?

double *edges = ...; // Each edge is defined by 4 doubles in the array.
struct Edge { double vals[4]; };
Edge *asStruct = reinterpret_cast<Edge*>(edges);
std::sort(asStruct, asStruct + edgesCount, EdgeLengthComparator());

特别是,sizeof(Edge) == sizeof(double)*4总是如此吗?它是否符合标准?如果没有,那么英特尔,AMD和ARM在实践中是否属实?你知道架构哪里不对吗?我已经在x86上测试了它,它在那里工作正常。

为什么我需要它如果你想知道为什么我需要这样的东西,我认为它是Sorting by blocks of elements with std::sort()的可能解决方案

编辑:我发现GCC有一个__attribute__((__packed__)),至少可以保证GCC吗? MSVC有类似的选择吗?

3 个答案:

答案 0 :(得分:4)

C标准不保证这一点。事实上,它明确地说(在6.7.2.1中):

  

结构或联合的末尾可能有未命名的填充

但是,实际上,您可以使用特定于实现的功能来强制对结构进行零填充。如果你这样做,我建议在编译代码时使用静态断言技术来确保sizeof(Edge) == sizeof(double)*4

更新(re C ++):

一系列评论询问我是否可以为C ++验证这一点。在draft copy of C++11,第220页,它说明了以下内容(强调我的):

  
      
  1. 指向标准布局结构对象的指针,使用reinterpret_cast进行适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单位),反之亦然。 [注意:因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头,以实现适当的对齐。 - 尾注]
  2.   

答案 1 :(得分:4)

  

标准是否有保障?

正式,不;在实践中,是的。最接近保证的是C ++ 11 9.2 / 20:

  

指向标准布局结构对象的指针(适当地使用reinterpret_cast转换)指向其初始成员(或者如果该成员是位字段,则指向它所在的单元)和副指针反之亦然。 [注意:因此,在标准布局结构对象中可能存在未命名的填充,但不是在其开头,以实现适当的对齐。 - 结束说明]

因此,演员将正确地将第一个数组元素重新解释为单个结构;但我认为没有正式的保证,它不会期望结构之间有额外的填充。

  

如果没有,那么英特尔,AMD和ARM在实践中是否正确?

至少在你列出的主流架构上,在这种情况下没有必要为对齐添加填充,没有其他理由这样做;所以在实践中,这应该有效,但没有保证可移植性。

  

我发现海湾合作委员会有一个__attribute__((__packed__)),至少可以保证它适用于海湾合作委员会吗?

是。根据{{​​3}},这个“使用最小所需内存来表示类型”,因此它将阻止在数组成员之后添加任何填充。

  

MSVC有类似的选择吗?

它有documentation /Zp1compiler flag #pragma pack(push, 1)#pragma pack(pop)恢复默认对齐方式),但它们似乎没有提供保证你需要;它们控制“第一个存储后的每个结构构件”的对齐,而没有提到整体结构尺寸。

答案 2 :(得分:1)

它可能起作用,也可能不起作用,或者可能发生任何奇怪的事情,因为会调用未定义的行为。对于与struct padding相关的信息,您可能还需要查看https://stackoverflow.com/a/5398498/2144471

但是,您可以通过这种方式进行重构,以便利用以下语言功能(http://en.cppreference.com/w/cpp/language/reinterpret_cast

  

只有在满足以下条件之一时,才能访问生成的指针或引用:
  ...
  T2是聚合类型或联合类型,它将上述类型之一保存为元素或非静态成员(包括,递归地,包含联合的子聚合和非静态数据成员的元素):这使得它可以安全地从结构的第一个成员和从联合的元素转换到包含它的结构/联合