什么是位域

维基百科 给出了以下解释

位域(或称“位段”,bit field)为一种数据结构,可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。

cppreference 给出了以下定义

Declares a class data member with explicit size, in bits. Adjacent bit field members may be packed to share and straddle the individual bytes.

这种数据结构的好处:

  • 可以使数据单元节省储存空间,当程序需要成千上万个数据单元时,这种方法就显得尤为重要。
  • 位段可以很方便的访问一个整数值的部分内容从而可以简化程序源代码。

而位域这种数据结构的缺点在于,其内存分配与内存对齐的实现方式依赖于具体的机器和系统,在不同的平台可能有不同的结果,这导致了位域在本质上是不可移植的。

位域的语法

下面就是位域的语法格式。

identifier(optional) attr(optional) : size

注意

  • size 的大小不能超过 identifier 所包含最大比特位个数。
  • identifier 为空时表示对应的 size 个数的比特位不使用
  • size 为0时表示根据前类型强制补齐

以下为代码示例。

struct S1 {
    // will usually occupy 2 bytes:
    // 3 bits: value of b1
    // 2 bits: unused
    // 6 bits: value of b2
    // 2 bits: value of b3
    // 3 bits: unused
    unsigned char b1 : 3, : 2, b2 : 6, b3 : 2;
};

struct S2 {
    // will usually occupy 2 bytes:
    // 3 bits: value of b1
    // 5 bits: unused
    // 6 bits: value of b2
    // 2 bits: value of b3
    unsigned char b1 : 3;
    unsigned char :0; // start a new byte
    unsigned char b2 : 6;
    unsigned char b3 : 2;
};

size 取0时如何理解

对于 size 取0时的各种情况进行了尝试,详细用例如下。思路是使用联合体能比较方便地将内存分布表示出来。
如下所示,定义了如下几种情况。

case1

union un1 {
    int n1;
    struct {
	int nn1 : 4;
	int : 0;
	int nn2 : 16;
    } st;
};

对联合体中的结构体位段进行赋值,并将联合体中的内容打印出来。

union un1 u1;
u1.st.nn1 = 1;
u1.st.nn2 = 1;
printf("union u1.n1=0x%08x sizeof(un1)=%zu\n", u1.n1, sizeof(un1));

输出结果为

union un1.n1=0x00000001 sizeof(un1)=8

case2

union un1 {
    int n1;
    struct {
	int nn1 : 4;
	char : 0;
	int nn2 : 16;
    } st;
};

输出结果为

union un1.n1=0x00000101 sizeof(un1)=4

case3

union un1 {
    int n1;
    struct {
	int nn1 : 4;
	short : 0;
	int nn2 : 16;
    } st;
};

输出结果为

union un1.n1=0x00010001 sizeof(un1)=4

case4

union un1 {
    int n1;
    struct {
	int nn1 : 8;
	char : 0;
	int nn2 : 16;
    } st;
};

输出结果为

union un1.n1=0x00000101 sizeof(un1)=4

structclass 关键字

多数例子都是以 struct 作为位域的组织标识,在C++中能否使用 class 作为位域的标识符呢?
经过测试,是可以使用 class 关键字的,但是需要注意 class 的默认访问控制属性为 private

位域的常见应用场景

为什么要使用位域?位域适合那些情况?

位域的主要使用目的是节省对象的内存使用。在存放一些比较小的数据时,使用位域能够使字节中的每个比特位合理地利用起来,避免内存浪费。

比较典型的应用是描述硬件寄存器。如果有32个一组的寄存器,每个寄存器代表一个比特位,就可以使用位域表示这组寄存器。

C++中的位操作接口

C++中也提供了一套位操作的接口 std::bitset ,这套接口提供了指定比特位数据的操作接口。