位域那些事
- 位域在内存中的布局是与机器有关的
- 位域的类型必须是整型或枚举类型,带符号类型中的位域的行为将因具体实现而定
- 取地址运算符(&)不能作用于位域,任何指针都无法指向类的位域
位域通常使用结构体声明, 该结构声明为每个位域成员设置名称,并决定其宽度:
例如声明如下一个位域:
{
unsigned int code1: 2;
unsigned int code3: 8;
};
struct _PRCODE prcode;
该定义使 prcode
包含 2 个 2 Bits 位域和 1 个 8 Bits 位域,我们可以使用结构体的成员运算符对其进行赋值
prcode.code1 = 0;
prcode.code2 = 3;
procde.code3 = 102;
赋值时要注意值的大小不能超过位域成员的容量,例如 prcode.code3 为 8 Bits 的位域成员,其容量为 2^8 = 256,即赋值范围应为 [0,255]。
例如以下位域:
struct box
{
unsigned int : 3;
unsigned int b: 4;
};
该位域结构体中间有一个未命名的位域,占据 3 Bits,仅起填充作用,并无实际意义。 填充使得该结构总共使用了 8 Bits。但 C 语言使用 unsigned int 作为位域的基本单位,即使一个结构的唯一成员为 1 Bit 的位域,该结构大小也和一个 unsigned int 大小相同。 有些系统中,unsigned int 为 16 Bits,在 x86 系统中为 32 Bits。文章以下均默认 unsigned int 为 32 Bits。
field1
+ field2
= 34 Bits,超出 32 Bits, 编译器会将field2
移位至下一个 unsigned int 单元存放, stuff.field1 和 stuff.field2 之间会留下一个 2 Bits 的空隙, stuff.field3 紧跟在 stuff.field2 之后,该结构现在大小为 2 * 32 = 64 Bits。
这个空洞可以用之前提到的未命名的位域成员填充,我们也可以使用一个宽度为 0 的未命名位域成员令下一位域成员与下一个整数对齐。例如:
struct stuff
{
unsigned int field1: 30;
unsigned int : 2;
unsigned int : 0;
};
这里 stuff.field1 与 stuff.field2 之间有一个 2 Bits 的空隙,stuff.field3 则存储在下一个 unsigned int 中,该结构现在大小为 3 * 32 = 96 Bits。
学习代码见:learn.cpp
位域的初始化与普通结构体初始化的方法相同,这里列举两种,如下:
struct stuff s1= {20,8,6};
或者直接为位域成员赋值
struct stuff s1;
s1.field1 = 20;
s1.field2 = 8;
s1.field3 = 4;
利用重映射将位域归零
int* p = (int *) &b1; // 将 "位域结构体的地址" 映射至 "整形(int*) 的地址"
*p = 0; // 清除 s1,将各成员归零
利用联合 (union) 将 32 Bits 位域 重映射至 unsigned int 型
先简单介绍一下联合
我们可以声明以下联合:
union u_box {
struct box st_box;
unsigned int ui_box;
};
x86 系统中 unsigned int 和 box 都为 32 Bits, 通过该联合使 st_box 和 ui_box 共享一块内存。具体位域中哪一位与 unsigned int 哪一位相对应,取决于编译器和硬件。利用联合将位域归零,代码如下:
u.ui_box = 0;
学习参考自: