【问题标题】:How does compiler determine the size of a bitfield struct?编译器如何确定位域结构的大小?
【发布时间】:2013-09-04 10:22:17
【问题描述】:

例如:

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_ 应该具有相同的长度,但似乎不是。

为什么?

【问题讨论】:

  • 对位字段使用uint32_t 之类的类型是不是有点傻? (一般来说,它们应该只在您需要完全匹配外部格式时使用,即使那样,它们的使用也是值得怀疑的。)

标签: c++ bit-fields


【解决方案1】:

标准(the working draft 的 9.6)是这样说的:

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

(我的重点)

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

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 个字节。

【讨论】:

  • 它是实现定义的,毫无疑问,但是当他的两个结构具有不同的大小时,它并不能说明实现的质量。
  • shrug 我认为我使用过的大多数实现都是这样的。它们将组合相似的字段,但不会组合不同的字段。在我检查之前,我实际上认为这就是标准中的定义。
  • 我认为我以前从未见过以这种方式工作的实现。 G++ 肯定不会。
【解决方案2】:

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

如果你想(需要)依赖对齐。 (如网络头处理)你需要使用#pragma push, pop。

或者 __ 属性 __(packed) - 这是一个 GCC 扩展。

struct {
...
} __attribute__(packed)

这将强制编译器压缩它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    • 2011-02-08
    • 2016-06-18
    • 1970-01-01
    • 2013-10-24
    • 1970-01-01
    相关资源
    最近更新 更多