【问题标题】:Bitfield union for easy bit access, unexpected behavior位域联合,便于位访问,意外行为
【发布时间】:2014-10-30 09:59:30
【问题描述】:

我做了这个联合,以便轻松地在位和整数之间来回访问。 (事实上​​,我寻找了一种方法来轻松地读取 / 写入有符号整数的所有位,没有可移植性陷阱,我把自己搞砸了)

typedef union {
    struct {
         unsigned int b32   : 1;
         unsigned int b31   : 1;
         unsigned int b30   : 1;
         unsigned int b29   : 1;
         unsigned int b28   : 1;
         unsigned int b27   : 1;
         unsigned int b26   : 1;
         unsigned int b25   : 1;
         unsigned int b24   : 1;

         /* etc. */

         unsigned int b01   : 1; /* LSB */
    } field;
    uint32_t word;
    int32_t integer;
} Bit32Field_T;

正如预期的那样,每个 bxx 字段都允许我访问底层 32 字节整数的位。

但是,此代码仅对 sparc(大端)CPU 有效。对于 little-endian (x86),我必须镜像从 b01 到 b32 的每个字段,以获得预期的行为。

typedef union {
    struct {
         unsigned int b01   : 1; /* LSB */
         unsigned int b02   : 1;
         unsigned int b03   : 1;
         unsigned int b04   : 1;
         unsigned int b05   : 1;
         unsigned int b06   : 1;
         unsigned int b07   : 1;
         unsigned int b08   : 1;
         unsigned int b09   : 1;

         /* etc. */

         unsigned int b32   : 1;
    } field;
    uint32_t word;
    int32_t integer;
} Bit32Field_T;

我认为 little-endianness 是关于反转字节而不是位!我应该如何理解做的位包装?

(一切都是用 gcc 编译的,对于 BE 在 solaris 下,对于 LE 在 debian 下)

【问题讨论】:

  • 这与 endienness 本身无关;位的排列似乎是compiler-dependent。如果你想要可移植的代码,你最好使用(x.word >> bit) & 1
  • 我自己解决了一个类似的问题,对我来说,将小端转换为大端是关于反转字节而不是位,我在反转位而不是字节之后意识到这一点..也许是关于你是否在一个 #define大/小系统然后是两个联合声明的适当#ifdef?不理想,因为它需要重建,但总比没有好?
  • 您的号码是 32 位整数。它由一行 4 个字节组成。这些字节在不同架构上的排序可能不同...
  • 1) 字节序问题通常是面向字节的问题,但位字节序问题确实存在。 2)为了最大的便携性,考虑unsigned可能有不同的大小。也许使用固定大小的,比如uint16_t 3) 为了获得最大的便携性,请遵循@M Oehm 的建议:使用轮班。
  • C 标准要求您的编译器记录它如何在位字段中对其位进行排序,因此您可以尝试查找该文档(如果不存在则提交错误报告)。跨度>

标签: c unions bit-fields


【解决方案1】:

我解决类似问题的方法是:将 32 位字从小端转换为大端:

unsigned char buf[4];
uint32_t word;

fread(buf, sizeof(buf), 1, fp);

word = buf[0] |
    (buf[1] << 8) |
    (buf[2] << 16) |
    (buf[3] << 24);

我同意你的代码工作很奇怪,我原以为 Pickif 建议的方法是需要的

如果 M Oehm 关于提取位的建议取决于编译器且与硬件无关,那么值得采纳 M Oehm 的建议并放弃联合方法

【讨论】:

  • 谢谢。我的问题是更多地了解位级别的镜像,而我期望它在字节级别 o_O 上。当然,我将我的代码切换到了 M OEhm 提案,即使它有时不太干净。
【解决方案2】:

对于小端,你必须这样做

typedef union {
struct {
         unsigned int b24   : 1;
         unsigned int b25   : 1;
         unsigned int b26   : 1;
         unsigned int b27   : 1;
         unsigned int b28   : 1;
         unsigned int b29   : 1;
         unsigned int b30   : 1;
         unsigned int b31   : 1;
         unsigned int b32   : 1;

         unsigned int b16   : 1;
         unsigned int b17   : 1;
         unsigned int b18   : 1;
         unsigned int b19   : 1;
         unsigned int b20   : 1;
         unsigned int b21   : 1;
         unsigned int b22   : 1;
         unsigned int b23   : 1;
         unsigned int b24   : 1;

         /* etc. */

         unsigned int b08   : 1; /* LSB */
    } field;
    uint32_t word;
    int32_t integer;
} Bit32Field_T;

【讨论】:

  • -1 这取决于编译器。在小端系统中没有一种真正的方法可以做到这一点。
  • 事实上,little endian / gcc 4 的测试和工作代码是在第二个块中找到的代码。具有连续的 b01 到 b32 字段。这正是我意想不到的:)
  • 您可能会争辩说,如果您编写的小端代码有效,那么您就有效地理解了位打包,即使它是出乎意料的
  • 我明白怎么做,而不是为什么 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-05
  • 2019-08-27
相关资源
最近更新 更多