【问题标题】:Can I use a union and bitfield for data packing我可以使用联合和位域进行数据打包吗
【发布时间】:2018-05-19 03:49:18
【问题描述】:

我正在为字体创建 4 位和 8 位颜色编码。这包括前景、背景、样式和格式。我希望使用以下结构来表示 4 字节包中的数据。我的意图是将其提取为单个uint32_t,可以将其转换为二进制数据并保存在文件中。

这是我目前拥有的:

struct font_pack {
    uint8_t : 8;
    struct {
        uint8_t format : 4;
        uint8_t style : 4;
    } header;
    uint8_t foreground;
    uint8_t background;
}

标头包含两个半字节。 format 表示颜色代码是 4 位或 8 位颜色。 style 是一个位标志集,用于声明格式化程序,例如粗体和下划线。

然后我使用以下union 获取原始二进制文件,用于写入文件以及将数据设置或打印为十六进制。

union font_raw {
    font_pack pack;
    uint32_t data;
}

不幸的是,当我打印出十六进制时,我得到了0x04032100,而我期待0x00120304。这让我觉得在 union 中不能保证字节对齐,并且字节序正在吸引我。我真的只是希望有简单的方法将数据打包和解包成 3 个字节。

有没有其他简单的方法可以做到这一点,还是我坚持使用更传统的功能来进行打包和拆包?

【问题讨论】:

  • 什么是sizeof(font_pack)
  • 通过使用C++标准uint8_t数据结构,font_pack是一个可靠的4字节。这完全符合联合中的uint32_t
  • 是什么让您认为它不起作用?我为字段分配值,将数据保存到 uint32_t,创建一个新的 font_raw,从 uint32_t 分配数据并且字段是正确的。为什么需要按特定顺序将其放入 3 个字节中?
  • 为了在二进制流中读取和写入数据,它需要按照正确的顺序,否则字段将根据编译器和平台而反转。所以现在对我来说,十六进制输出的顺序与 font_pack 的声明顺序相反。

标签: gcc c++14


【解决方案1】:

这看起来肯定是字节顺序问题。我猜您使用的是 x86/x64(类似 Intel)架构,它是 little-endian 并且会将字节从最不重要到最重要打包。如果您在同一架构(如此小端系统)上写入和读取数据,则字节序将确保您以相同的顺序读取字节打包,因此您的 font_pack 成员仍应正确输出。但是,如果您要在大端系统上加载这些文件,则需要走更传统的路线。但是,如果您保证使用相同的字节序,我会采用您的方法。它非常优雅:)

edit:如果您正在在不同的字节序机器之间进行读取或写入,那么您总是可以执行以下操作:

#ifdef LITTLE_ENDIAN
struct font_pack {
    uint8_t : 8;
    struct {
        uint8_t format : 4;
        uint8_t style : 4;
    } header;
    uint8_t foreground;
    uint8_t background;
}
#else
struct font_pack {
    uint8_t background;
    uint8_t foreground;
    struct {
        uint8_t format : 4;
        uint8_t style : 4;
    } header;
    uint8_t : 8;
}
#endif

然后在您的 x86 或类似系统上定义 LITTLE_ENDIAN,而不是在大端系统上。希望对您有所帮助。

【讨论】:

  • 有道理,我猜如果您为特定架构(即 x86_64)编译,尽管它运行在物理硬件上,但字节序仍将得到保证。
  • 是的。记住这一点并没有什么坏处,尤其是在编写跨平台代码时。我以前被这个咬过(有人用 PowerPC 吗?)
猜你喜欢
  • 2019-12-16
  • 1970-01-01
  • 1970-01-01
  • 2020-12-14
  • 1970-01-01
  • 2018-03-26
  • 1970-01-01
  • 2017-11-02
  • 1970-01-01
相关资源
最近更新 更多