在二进制反序列化中使用bitfield-struct:在GCC上保证布局吗?

kgqe7b3p  于 2023-01-20  发布在  其他
关注(0)|答案(2)|浏览(161)

所以,我写了一个结构体,它将被用来反序列化一个二进制数据流。为了让大家明白这一点,这里有一个精简版:

typedef struct
{
    bool flag1 : 1;
    bool flag2 : 1;
    bool flag3 : 1;
    bool flag4 : 1;
    uint32_t reserved : 28;
} frame_flags_t;

typedef struct
{
    /* Every frame starts with a magic value. */
    uint32_t magic;

    frame_flags_t flags;

    uint8_t reserved_1;
    
    /* A bunch of other things */

    uint32_t crc;
} frame_t;

我的问题是,如果做到以下几点:

frame_t f;

memcpy(&f, raw_data_p, sizeof(frame_t));

我能保证f.flags.flag1确实是第一位(在magic成员之后,假设是一个整齐 Package 的结构体(它是这样的))吗?.flags2将是其后的一位,等等?
据我所知,C和C++标准并不能保证这一点。

pbpqsu0x

pbpqsu0x1#

我能保证f.flags.flag1确实是第一位(在magic成员之后,假设是一个整齐 Package 的结构体(它确实是))吗?
C语言不能保证这一点。
.flags2将是接下来的一个,等等?
C语言 * 确实 * 要求分配给同一个可寻址存储单元的连续位域之间没有间隙。这可能意味着标志最终占据了同一个字节中的相邻位,但它并不 * 必须 * 意味着这一点。
据我所知,C和C++标准并不能保证这一点。
不可以。结构布局规则是 * 应用程序二进制接口 * 的特征(ABI),这是操作系统+硬件组合的一个属性。例如,对于运行在x86_64上的Linux有一个ABI,对于运行在32位x86上的Linux有另一个ABI,对于运行在这些平台上的Windows则仍然不同。GCC支持各种ABI,并且它根据目标ABI的规则来布置结构,它不能对结构布置的细节做出任何全面的保证。
例如:ABI for Linux/x86_64https://www.intel.com/content/dam/develop/external/us/en/documents/mpx-linux64-abi.pdf,位域布局为:
位字段遵守与其他结构和联合成员相同的大小和对齐规则。
还有:

  • 位字段从右到左分配
  • 位字段必须包含在适合其声明类型的存储单元中
  • 位字段可以与其他结构/联合成员共享存储单元

这实际上并不完全一致,但对于frame_flags_t,它的解释方式是:

  • 该结构大小为4,包括一个大小为4的单个"可寻址存储单元",所有的位字段都被封装到该存储单元中
  • flag1使用最低有效位
  • flag2使用次低有效位
  • flag3使用第三低有效位
  • flag4使用第四低有效位

此外,整个frame_t结构在Linux/x86_64上有4字节对齐要求,并且它将使用对齐所有成员所需的最小填充进行布局。因此,在这样的计算机上,magic成员和flags成员之间将没有填充。x86_64是little-endian,因此,在Linux/x86_64**上,这确实会将标志位放在magic**之后的第一个字节中。

jtjikinw

jtjikinw2#

这也适用于ARM v7,
在这个目标上,您可以安全地使用位字段,并且ABI明确指定了它们的行为。

相关问题