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)
位域比显式位掩码使用起来更方便,尤其是对于长度大于 1 的情况。手动编码的位操作经常以微妙的方式被破坏。
位域的主要问题是不精确的规范:
无论int类型位域符号或无符号是实现定义。
内存中位域的顺序和位置是实现定义的,这使得硬件映射更具风险。即使对于给定的字节序,位域的位置和顺序也没有精确指定,这是一个主要缺点。
请特别注意这int field_a : 1;实际上是有问题的:int具有单个位的位域可能会被读取为-1而不是1if 设置。你应该使用unsigned int field_a : 1;等。
我知道可以使用位掩码来隔离数据结构中的某些位,但为什么还要费心使用位域呢?
使用位域可以映射其字段大小与 C 实现的内置类型(例如 TCP 标头)不完全匹配的数据结构,或者只是缩小数据结构的大小。
您确实可以通过使用掩码和移位来手动打包和解包数据,但位域为此提供了更方便的语法。除了隐藏移位和掩码之外,位域访问还可以_Bool在适当的情况下透明地处理符号扩展问题和 的特殊特性。
代价是失去对细节的控制。如果您手动打包和拆包,那么您可以对布局有完全的信心和控制,并且具有极大的便携性。另一方面,如果您使用位域,并且您关心位如何排列的细节,那么您需要依靠实现细节或扩展来确保您拥有您想要的布局,如果这甚至可以从您的完全实施。