位域 VS。位掩码

nan*_*man 4 c bit-manipulation bitmask bit-fields

我以前在 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

chq*_*lie 5

位域比显式位掩码使用起来更方便,尤其是对于长度大于 1 的情况。手动编码的位操作经常以微妙的方式被破坏。

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

  • 无论int类型位域符号或无符号是实现定义。

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

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


Joh*_*ger 1

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

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

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

代价是失去对细节的控制。如果您手动打包和拆包,那么您可以对布局有完全的信心和控制,并且具有极大的便携性。另一方面,如果您使用位域,并且您关心位如何排列的细节,那么您需要依靠实现细节或扩展来确保您拥有您想要的布局,如果这甚至可以从您的完全实施。