联盟内部的位域对齐

时间:2012-09-12 08:05:20

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

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

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个字节。 如果我尝试将union定义为“packed”,同样的事情会发生 - size仍然是4.这个行为是否符合C ++规范?会有什么解释?

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

3 个答案:

答案 0 :(得分: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

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

答案 1 :(得分:0)

嗯,首先,__attribute__((__packed__))是一个gcc扩展,所以标准没有任何关于可能会或可能不会发生的事情。

但是,通常,允许编译器在位字段之间插入任何适当的填充。特别是,我见过这样的编译器:

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

将b对齐32位边界。

基本上,如果你使用位域,你就是自己的。无法保证字段或填充的顺序。您唯一的保证是位域的大小。

答案 2 :(得分:0)

这个问题被标记为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,它将始终映射到最接近的整数类型,即整数。