10. 位域

位域(bit-fields) 也叫位段,是在一个结构体内部,使用 位(bit) 来划分边界,将划分出来的位重新定义成为结构体成员变量来代替位操作一种方式。位域是减少内存消耗,有效利用内存空间,提高开发效率的方法。

位域能够解决的问题:

在 C 语言中,如果使用一个 char 类型的变量来表示男(用1表示)和女(用0表示),则只用到了一个字节的最低位,而高位 7 位都是零。这样比较浪费内存空间。实际 一个 char 类型表示一个字节,由 8 个位组成。我们可以用 一个 char 类型来表示 8 个布尔值。或用一个 char 类型中的 1 个位表示男女,1个位表示是否已婚,1个位保留不用,2个位来表示是体重等级(过轻、正常、超重、严重超重),3个位表示信用等级。这样可以充分利用这段内存。

位域的语法

struct [结构体名] {
    数据类型 [成员变量名1] : 占用位宽1, [成员变量名2] : 占用位宽2, ...;
    // ... 结构体内其它成员或位域
} [变量名1][,变量名2, ...];

说明

  1. 结构体名必须是标识符。
  2. 占用位宽必须是整数常量表达式,表示成员变量占用的位数。
  3. 成员变量名是占用位数的别名,可以通过成员变量访问该成员变量占用的位。
  4. 成员变量名可以省略不写,省略不写时占用的位不可使用。
  5. 数据类型 是 该成员变量占用的位在引用时作为的数据类型,把这几位当成什么类型来处理。
    • 当数据类型是有符号类型时,占用位宽的高位会视为符号位(需要注意)。
  6. 结构体可以在声明直接定义变量。也可以在声明后再定义变量。

示例:

// filename: bit_field.c
#include <stdio.h>

struct human {
    unsigned char gender : 1, married: 1;
    unsigned char : 1;  // 该1位保留不用
    unsigned char  weight: 2, credit: 3;
};

int main(int argc, char * argv[]) {
    struct human h = {
            .gender = 1,  // 性别
            .married = 0,  // 是否已婚
            .weight = 2,  // 体重指数
            .credit = 5  // 信用评级
        };

    printf("sizeof(struct human): %ld\n", sizeof(struct human));

    printf("gender: %d\n", h.gender);
    printf("married: %d\n", h.married);
    printf("weight: %d\n", h.weight);
    printf("credit: %d\n", h.credit);

    return 0;
}

编译和运行结果如下:

weimingze@mzstudio:~$ gcc -o bit_field bit_field.c
weimingze@mzstudio:~$ ./bit_field
sizeof(struct human): 1
gender: 1
married: 0
weight: 2
credit: 5

可见上述结构体创建的变量只有一个字节,但可以当成 四个变量来用,每个成员变量使用这个字节中的某一位或某几位。

注意事项:

实验:

尝试使用位域和普通的结构体成员变量混合使用。然后测试你编译器是如何字节对齐的,并画出内存结构图。