确定与其成员相关的C/C++结构的对齐方式

Hrv*_*eša 25 c c++ alignment

如果已知结构构件的对齐,是否可以找到结构类型的对齐?

例如.对于:

struct S
{
 a_t a;
 b_t b;
 c_t c[];
};
Run Code Online (Sandbox Code Playgroud)

是S = max(alignment_of(a),alignment_of(b),alignment_of(c))的对齐?

在网上搜索我发现"对于结构化类型,其任何元素的最大对齐要求决定了结构的对齐"(在每个程序员应该知道的内存中)但我在标准中找不到任何类似的东西(最新草稿更准确).


编辑: 非常感谢所有的答案,特别是罗伯特·甘博为原始问题提供了一个非常好的答案,以及其他贡献者.

简而言之:

为了确保结构构件的对准要求,结构的对准必须至少与其最严格构件的对准一样严格.

至于确定结构的对齐方式,我们提出了一些选项,经过一些研究,我发现了这个:

  • c ++ std :: tr1 :: alignment_of
    • 尚未标准,但关闭(技术报告1),应该在C++ 0x中
    • 最新草案中存在以下限制:前提条件:T应为完整类型,引用类型或未知范围的数组,但不应为函数类型或(可能是cv-qualified)void.
      • 这意味着我提出的使用C99灵活数组的用例将无法工作(这并不奇怪,因为灵活的数组不是标准的c ++)
    • 在最新的c ++草案中,它是用新关键字的术语定义的 - alignas(具有相同的完整类型要求)
    • 在我看来,如果c ++标准曾经支持C99灵活数组,那么要求可以放宽(结构与灵活数组的对齐不应该根据数组元素的数量而改变)
  • c ++ boost :: alignment_of
    • 主要是tr1替代品
    • 似乎是专门针对void而在这种情况下返回0(这在c ++草案中是禁止的)
    • 来自开发人员的注意事项:严格来说,您应该只依赖于ALIGNOF(T)的值是T的真实对齐的倍数,尽管在实践中它确实在我们所知道的所有情况下计算了正确的值.
    • 我不知道这是否适用于灵活的数组,它应该(可能不起作用,这解决了我的平台上的编译器内在因此我不知道它将如何在一般情况下表现)
  • Andrew Top提出了一个简单的模板解决方案,用于计算答案中的对齐方式
    • 这似乎与boost正在做的非常接近(如果它小于计算的对齐,则boost会另外返回对象大小,因为我可以看到相同的通知
    • 这适用于灵活的数组
  • 使用Windbg.exe找出符号的对齐方式
    • 不编译时,编译具体,没有测试它
  • 在包含该类型的匿名结构上使用offsetof
    • 看到答案,不可靠,不能用c ++非POD移植
  • 编译器内在函数,例如.MSVC __alignof
    • 适用于灵活的数组
    • alignof关键字在最新的c ++草案中

如果我们想要使用"标准"解决方案,我们只限于std :: tr1 :: alignment_of,但如果你将c ++代码与c99的灵活数组混合,这将不起作用.

我认为它只有一个解决方案 - 使用旧的struct hack:

struct S
{
 a_t a;
 b_t b;
 c_t c[1]; // "has" more than 1 member, strictly speaking this is undefined behavior in both c and c++ when used this way
};
Run Code Online (Sandbox Code Playgroud)

在这种情况下(以及其他所有情况),不同的c和c ++标准及其不断增长的差异是不幸的.


另一个有趣的问题是(如果我们无法以便携方式找出结构的对齐方式)可能的最严格的对齐要求是什么.我可以找到几种解决方案:

  • boost(内部)使用各种类型的并集,并在其上使用boost :: alignment_of
  • 最新的c ++草案包含std :: aligned_storage
    • 对于任何大小不大于Len的C++对象类型,default-alignment的值应该是最严格的对齐要求
      • 所以std::alignment_of< std::aligned_storage<BigEnoughNumber>>::value应该给我们最大的一致性
      • 仅草案,尚未标准(如果有的话),tr1::aligned_storage没有这个属性

对此的任何想法也将不胜感激.

我暂时取消选中接受的答案,以获得更多关于新子问题的可见性和输入

Rob*_*ble 26

这里有两个密切相关的概念:

  1. 处理器访问特定对象所需的对齐方式
  2. 编译器实际用于将对象放入内存的对齐方式

为了确保结构构件的对准要求,结构的对准必须至少与其最严格构件的对准一样严格.我不认为标准中明确说明了这一点,但可以从以下事实中推断出来(在标准中单独列出):

  • 结构允许有其成员之间的填充(和结束)
  • 数组是容许有自己的元素之间填充
  • 您可以创建任何结构类型的数组

如果结构的对齐方式至少与其每个成员一样严格,则无法创建结构数组,因为某些结构成员的某些元素将无法正确对齐.

现在,编译器必须根据其成员的对齐要求确保结构的最小对齐,但它也可以以比所需更严格的方式对齐对象,这通常是出于性能原因而进行的.例如,许多现代处理器将允许以任何对齐方式访问32位整数,但如果它们未在4字节边界上对齐,则访问可能会明显变慢.

没有可移植的方法来确定处理器对任何给定类型强制执行的对齐,因为语言没有公开这种对齐,尽管由于编译器显然知道目标处理器的对齐要求,因此它可以将此信息公开为扩展.

虽然许多编译器可以选择对对齐提供某种程度的控制,但也没有可移植的方式(至少在C中)来确定编译器如何实际对齐对象.


小智 13

我编写了这种类型的特征代码来确定任何类型的对齐方式(基于已经讨论过的编译器规则).您可能会发现它很有用:

template <class T>
class Traits
{
public:
    struct AlignmentFinder
    {
        char a; 
        T b;
    };

    enum {AlignmentOf = sizeof(AlignmentFinder) - sizeof(T)};
};
Run Code Online (Sandbox Code Playgroud)

所以现在你可以去:

std::cout << "The alignment of structure S is: " << Traits<S>::AlignmentOf << std::endl;
Run Code Online (Sandbox Code Playgroud)


Mic*_*urr 6

以下宏将返回任何给定类型的对齐要求(即使它是结构):

#define TYPE_ALIGNMENT( t ) offsetof( struct { char x; t test; }, test )
Run Code Online (Sandbox Code Playgroud)

注意:我可能在过去的某个时候从微软的标题中借用了这个想法......


编辑:正如Robert Gamble在评论中指出的那样,这个宏不能保证有效.实际上,如果将编译器设置为在结构中打包元素,它肯定不会很好地工作.因此,如果您决定使用它,请谨慎使用.

有些编译器有一个扩展,允许你获得一个类型的对齐(例如,从VS2002开始,MSVC有一个__alignof()内在的).那些应该在可用时使用.