【问题标题】:Bitfields VS. Bitmasks位域 VS。位掩码
【发布时间】:2019-11-19 07:33:00
【问题描述】:

我以前在 C 方面有过一些经验,但是我以前从未见过位域功能。我知道可以使用位掩码来隔离数据结构中的某些位,但是为什么还要使用位域呢?

例如,假设我们要隔离的位是前 3 个最低有效位。然后我们可以写:

/* our bitmasks */
#define FIELD_A (1 << 0)
#define FIELD_B (1 << 1)
#define FIELD_C (1 << 2)

int main(void) 
{ 
    /* the data structure that contains our three fields */
    uint8_t flags;

    /* accessing field A (as a boolean) */
    int bool_a = !!(flags & FIELD_A);

    /* accessing field B (as a boolean) */
    int bool_b = !!(flags & FIELD_B);

    /* accessing field C (as a boolean) */
    int bool_c = !!(flags & FIELD_C);

    return 0;
}

为什么我们会选择这样写:

static struct fields {
    int field_a : 1;
    int field_b : 1;
    int field_c : 1;
};

int main(void) 
{ 
    /* the data structure that contains our three fields */
    struct fields flags;

    /* accessing field A */
    int bit_a = flags.a;

    /* accessing field B */
    int bit_b = flags.b;

    /* accessing field C */
    int bit_c = flags.c;

    return 0;
}

【问题讨论】:

  • 位掩码方法更加便携和健壮。坚持下去。尽管位域看起来很方便。除非你不关心位的布局。
  • 您的第一个示例不正确。您希望按用于在 #define 中移动的数字进行移动,而不是按 FIELD_X 本身。
  • @P__J__ 对于任何“非便携式”声明都是如此。但是在位域的情况下,甚至无法从代码中清楚地看出哪个位是 LSB,哪个是 MSB。也不清楚整个位域的宽度是多少。还有一些其他有趣的事情,只有通过实验或深入挖掘编译器文档才能发现。
  • @P__J__ 编码时,迂腐获得第一。
  • int field_a : 1; 实际上是不正确的:如果设置了单个位,int 位域可能会被读取为 -1 而不是 1。你应该写unsigned int field_a : 1;

标签: c bit-manipulation bit-fields bitmask


【解决方案1】:

位域比显式位掩码更易于使用,尤其是对于长度大于 1 的位域。手动编码的位旋转经常以微妙的方式被破坏。

位域的主要问题是不精确的规范:

  • int 类型位域是有符号还是无符号由实现定义。

  • 位域在内存中的顺序和位置是实现定义的,这使得硬件映射风险更大。即使对于给定的字节序,位域的位置和顺序也没有精确指定,这是一个主要缺点。

请特别注意int field_a : 1; 实际上是有问题的:如果设置了一个位,则具有单个位的int 位域可能会被读取为-1 而不是1。您应该使用unsigned int field_a : 1; 等。

【讨论】:

  • 也许我对标准的解释在这里不正确,但我认为如果您没有明确限定位域的签名,则它只是定义是否对位字段进行签名的实现。从 C11 标准 6.7.2.1p5 开始:位字段的类型应为 _Boolsigned intunsigned int 或其他一些实现定义的类型的合格或非合格版本。
  • 结合脚注 125 以获得更清晰的画面:如上面 6.7.2 中所述,如果使用的实际类型说明符是 int 或定义为 typedef-name 的 @987654332 @,然后由实现定义位域是有符号还是无符号。
【解决方案2】:

我知道可以使用位掩码来隔离数据结构中的某些位,但是为什么还要使用位域呢?

使用位域来映射字段大小与 C 实现的内置类型(例如 TCP 标头)不完全匹配的数据结构,或者只是缩小数据结构的大小。

您确实可以通过使用掩码和移位手动打包和解包数据,但位域为此提供了更方便的语法。除了隐藏移位和屏蔽之外,位域访问还可以在适当的情况下透明地处理符号扩展问题和_Bool 的特殊特性。

权衡是失去对细节的控制。如果您手动打包和解包,那么您可以完全自信地控制布局,具有很好的便携性。另一方面,如果您使用位域,并且您关心位如何排列的细节,那么您需要依靠实现细节或扩展来确保您拥有所需的布局,如果您的实现完全可以做到的话。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 1970-01-01
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多