【问题标题】:C++ size of structure with bit fields具有位域的结构的 C++ 大小
【发布时间】:2016-01-28 08:22:03
【问题描述】:

我有以下三个联合:

typedef union {
    struct {
        uint16_t : 2;
        uint16_t numberOfWords : 10;
        uint16_t  : 4;
        uint16_t dataFormat : 8;
        uint16_t : 8;
    } bf;
    uint32_t dw;
} HeaderT;

typedef union {
    struct {
        uint16_t : 4;
        uint16_t lsb : 8;
        uint16_t  : 4;
        uint16_t msb : 8;
        uint16_t : 8;
    } bf;
    uint32_t dw;
} RegisterT;

typedef union {
    struct {
        uint16_t : 2;
        uint16_t lsb : 10;
        uint16_t : 2;
        uint16_t msb : 10;
        uint16_t : 8;
    } bf;
    uint32_t dw;
} BinT;

我得到 sizeof(HeaderT) == 4,sizeof(RegisterT) == 4,但 sizeof(BinT) == 8!我不知道为什么。

在做

typedef union {
    struct {
        uint16_t : 2;
        uint16_t lsb : 10;
        uint16_t : 2;
        uint16_t msb : 10;
        uint16_t : 8;
    } bf __attribute__((packed));
    uint32_t dw;
} BinT;

没有帮助。我需要 BinT 为 32 位宽;它被内存映射到 FPGA 上的一堆寄存器。

有人知道发生了什么吗?我在 ARMv7 上使用 gcc。但是,我在 x86_64 VirtualBox VM 上的 gcc 上看到了相同的情况。

谢谢。

【问题讨论】:

  • 好的,所以通过将 uint16_t 的全部更改为无符号,我得到了预期的结果。不过,我不明白为什么。类型不是仅在从位字段读取时才相关[假设类型足够宽],例如,如果您从 uin16_t 位字段读取,您会得到一个包含这些位的 uint16_t?
  • 因为您使用uint16_t,单个位域中的位不能超过 16 位的边界。 BinT 的前 3 个字段中使用了 14 位,因此接下来的 10 位不适合 16,因此它为 msb 中的 10 位启动一个新的 uint16_t。然后 8 个匿名位在 10 之后不适合,所以你有第三个 uint16_t 单元。把最后一组uint16_t改成uint32_t就OK了。

标签: c++ gcc arm


【解决方案1】:

位域成员不能分成两个(或更多)原语。好吧,就标准而言,根本不保证任何包装。它是实现定义的。但这是一个典型的限制,如果实现确实打包了位字段。

让我们看看GCC manual 对实现定义的行为有何评论:

  • 位域是否可以跨越存储单元边界(C90 6.5.2.1、C99 和 C11 6.7.2.1)。

由 ABI 确定。

我不确定这是否适用于 ARM,但通常 GCC 符合 64-bit Itanium spec

(假设从左到右打包)您的原语之间的“位分布”现在是:

uint16_t: 2 10 2 // 10 won't fit anymore
uint16_t: 10     // 8 won't fit anymore
uint16_t: 8

这三个uint16_t 不可能适合 32 位。

这个:

union BinT {
    struct {
        uint32_t : 2;
        uint32_t lsb : 10;
        uint32_t : 2;
        uint32_t msb : 10;
        uint32_t : 8;
    } bf;
    uint32_t dw;
};

应该可以工作,因为现在所有位域都可以共享一个原语。

好的,所以通过将 uint16_t 全部更改为无符号,我得到了预期的结果。不过我不明白为什么

unsigned 将显示为 32 位宽。

我需要 BinT 为 32 位宽

鉴于该要求,如果您希望您的程序具有可移植性,您可能应该避免使用位字段。如果你使用位域,那么你依赖于实现定义的行为。

【讨论】:

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