编译器如何确定位域结构的大小?

时间:2013-09-04 10:22:17

标签: c++ bit-fields

例如:

struct a {
    uint32_t    foreColor_ : 32;
    uint32_t    backColor_ : 32;
    uint16_t    lfHeight_ : 16;
    uint16_t    flags_: 4;
    bool    lfBold_: 1;
    bool    lfItalic_: 1;
    bool    lfUnderLine_: 1;
    bool    lfDashLine_: 1; 
    bool    lfStrike_: 1;
    bool    lfSubscript_: 1;
    bool    lfSuperscript_: 1;
};

是16个字节,但

struct a {
    uint32_t    foreColor_ : 32;
    uint32_t    backColor_ : 32;
    uint16_t    lfHeight_ : 16;
    uint8_t     flags_: 4;
    bool    lfBold_: 1;
    bool    lfItalic_: 1;
    bool    lfUnderLine_: 1;
    bool    lfDashLine_: 1; //for ime 
    bool    lfStrike_: 1;
    bool    lfSubscript_: 1;
    bool    lfSuperscript_: 1;
};

长12个字节。

我认为flags_应该有相同的长度,但似乎没有。

为什么?

2 个答案:

答案 0 :(得分:6)

标准(the working draft的9.6)说明了这一点:

  

指定位字段;它的长度是从位字段名称中设置的   通过冒号。位字段属性不是类型的一部分   班级成员。常量表达式应该是一个整数   常量表达式,其值大于或等于零。该   常量表达式可能大于中的位数   对象表示(   3.9)比特字段的类型;在这种情况下,额外的比特用作填充比特,不参与值表示(   3.9)位域。 类对象中位域的分配是实现定义的。位域的对齐是   实现定义即可。比特字段被打包成一些可寻址的字段   分配单位。 [注意:位字段跨越一些分配单元   机器而不是其他机器。位字段从右到左分配   一些机器,从左到右在其他机器上。 - 后注]

(我的重点)

所以它取决于你的编译器。在您的情况下似乎正在发生的事情 - 我将其描述为相当正常的行为 - 是它只是组合相同类型的位域然后将结构打包到4字节边界,所以在第一种情况下我们有:< / p>

struct a {
    uint32_t    foreColor_ : 32; // 4 bytes (total)
    uint32_t    backColor_ : 32; // 8 bytes
    uint16_t    lfHeight_ : 16;  // 10 bytes
    uint16_t    flags_: 4;       // 12 bytes
    bool    lfBold_: 1;          // 13 bytes
    bool    lfItalic_: 1;
    bool    lfUnderLine_: 1;
    bool    lfDashLine_: 1; 
    bool    lfStrike_: 1;
    bool    lfSubscript_: 1;
    bool    lfSuperscript_: 1;   // still 13 bytes
};

然后填充到16个字节,在第二个字节中我们有:

struct a {
    uint32_t    foreColor_ : 32;  // 4 bytes (total)
    uint32_t    backColor_ : 32;  // 8 bytes
    uint16_t    lfHeight_ : 16;   // 10 bytes
    uint8_t     flags_: 4;        // 11 bytes
    bool    lfBold_: 1;           // 12 bytes
    bool    lfItalic_: 1;
    bool    lfUnderLine_: 1;
    bool    lfDashLine_: 1; 
    bool    lfStrike_: 1;
    bool    lfSubscript_: 1;
    bool    lfSuperscript_: 1;    // still 12 bytes
};

哪个不需要填充,并保持12个字节。

答案 1 :(得分:0)

它是特定于编译器的,编译器正在进行各种对齐以优化对字段的访问。

如果你想(需要)依赖于对方。 (比如网络标题处理)你需要使用#pragma push,pop。

或__ attribute __(packed) - 这是一个GCC扩展名。

struct {
...
} __attribute__(packed)

这将迫使编译器压缩它。

相关问题