4

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

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++ 规范”。

4

3 回答 3

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

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

于 2012-09-12T08:39:52.940 回答
0

好吧,首先,__attribute__((__packed__))它是一个 gcc 扩展,因此该标准没有任何关于可能发生或可能不会发生的事情的说法。

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

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

将 b 在 32 位边界上对齐。

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

于 2012-09-12T08:44:07.620 回答
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,它将始终映射到最接近的整数类型,即整数。

于 2012-09-12T08:47:49.140 回答