【问题标题】:Alignment of bitfields inside unions联合内的位域对齐
【发布时间】:2012-09-05 05:01:59
【问题描述】:

我对以下代码如何在内存中布局感到有些困惑:

struct Thing
{
    union
    {
        unsigned value:24;
        uint8_t bytes[3];
    };
    Thing(int v)
            :value(v)
    {}

    void foo()
    {
        printf("Thing %p value=%d !\n", this, value);
    }

} __attribute__((__packed__));

在 Linux 上的 gcc 3.3、4.3 或 4.6 上(没有我能想到的任何特殊选项 - 只有 4.6 上的“-Wall -g”),结构的大小始终为 4:

$ pahole ./union
struct Thing {
        union {
                unsigned int               value;                /*           4 */
                unsigned char              bytes[3];             /*           3 */
        };
[...]

我们在这里有一些类似的代码,其中我们在结构中有无符号值:24,有人添加了联合,无意中将结构的大小从 3 个字节增加到 4 个字节。 如果我尝试将联合定义为“打包”,也会发生同样的事情——大小仍然是 4。这种行为是否符合 C++ 规范?会有什么解释?

稍后编辑:将“C 规范”替换为“C++ 规范”。

【问题讨论】:

    标签: c++ gcc unions bit-fields memory-layout


    【解决方案1】:

    您错过了匿名联合的打包属性。考虑这个例子:

    #define PACKED __attribute__((__packed__))
    struct S1 { unsigned value:24; } PACKED ;
    struct S2 { union { char a[3]; unsigned value:24; };  } PACKED ;
    struct S3 { union { char a[3]; unsigned value:24; } PACKED ;  };
    
    
    int main() {
       std::cout << sizeof(S1) << std::endl;
       std::cout << sizeof(S2) << std::endl;
       std::cout << sizeof(S3) << std::endl;
    }
    

    输出:

    3
    4
    3
    

    packed 属性有点奇怪,我总是尝试测试所有可能的组合以获得正确的结果。

    【讨论】:

    • 哎呀,你是对的! pahole 显示 value 字段的长度为 4 个字节,但进一步向下:
    •  /* size: 3, cachelines: 1, members: 1 / / last cacheline: 3 bytes */ 
    • 我从来没有用过这个pahole工具。我总是依赖 sizeof 和 gcc 扩展:__alignof__。 C++11 中的最后一个是标准的一部分 - 但我的工作中有旧的 gcc (4.4.x)。
    • 当然 sizeof 和 alignof 也可以,pahole 可以省去打印结构尺寸的工作;它还有助于找到可以打包在结构/类中的漏洞。我们将它与旧的 (gcc-4.3) 和古老的 (gcc-3.3) 工具链一起使用,它工作正常,尽管有时(不经常)在查看 C++ 时会遇到这种情况。
    【解决方案2】:

    嗯,首先,__attribute__((__packed__)) 是一个 gcc 扩展,因此标准没有说明可能会发生什么或不会发生什么。

    然而,一般来说,编译器可以在位域之间插入它认为合适的任何填充。特别是,我看到编译器给出了这个:

     struct
     {
         short a : 8;
         int b : 8;
     }
    

    将 b 在 32 位边界上对齐。

    基本上,如果您使用位域,则只能靠自己。不能保证字段或填充的顺序。您唯一的保证是位域的大小。

    【讨论】:

      【解决方案3】:

      这个问题被标记为 C++,但你提到了 C 规范。 AFAIK,在这方面 C 和 C++ 是有区别的。 在 C 中,位域只能是整数类型,而 C++ 允许任何整数类型。

      由于位域映射到整数类型,我希望位域的大小始终为 1、2 或 4。

      因此,这会给你 sizeof 3:

      struct Thing
      {
          union
          {
              // without 'short' int would be assumed -> 4 bytes
              unsigned short value:15; // -> maps to short -> 2 bytes
              uint8_t bytes[3];        // -> 3 bytes, so the whole union is 3 bytes long
          };
      }
      

      使用value:24,它将始终映射到最接近的整数类型,即整数。

      【讨论】:

      • 是的,对不起,我的意思是 C++。我可能应该更正这个问题 - 我知道 C 和 C++ 规范并不总是一致的。
      猜你喜欢
      • 1970-01-01
      • 2012-02-09
      • 1970-01-01
      • 1970-01-01
      • 2015-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      相关资源
      最近更新 更多