C 结构体中的位域概念

位域

一、基本概念

1.1 位域的概念

结构体中的冒号表示位域,如:

C 复制代码
struct bit_struct
{
    unsigned int  bit1:10;
    unsigned int  bit2:8;
    unsigned int  bit3:14;
} data1; // sizeof data1 = 4

struct bit_struct
{
    unsigned int  bit1:10;
    unsigned int  bit2:8;
    unsigned int  bit3:16;
} data2; // sizeof data2 = 8

位域出现的原因是由于某些信息的「存储表示」只需要几个 bit 位就可以表示,而不需要一个完整的字节,同时也是为了节省存储空间和方便处理。

1.2 说明

  1. 位域必须存储在同一个类型中,不能跨类型,同时也说明位域的长度不会超过所定义类型的长度。如果一个定义类型单元里所剩空间无法存放下一个域,则下一个域应该从下一单元开始存放。

    如 data2,所定义的类型是 unsigned int 类型,一共 32 位,bit1 和 bit2 用掉了 18bit,还剩下 \(32-18=14bit\),这时要存储一个 16bit 的位域元素 bit3,那么这个元素就只能从下一个 unsigned int 类型的单元开始而不会在前面一个unsigned int 类型中占 14bit 后面的 unsigned int 类型中占 2bit。

  2. 如果位域的位域长度为0表示是个空域,同时下一个域应当从下一个字节单元开始存放。

  3. 使用无名的位域来作为填充和调整位置,切记该位域是不能被使用的。

  4. 位域的本质上就是一种结构体类型,不同的是其成员是按二进制位来分配的。

二、代码理解

C 复制代码
#include <stdio.h>
#include <string.h>

struct bit_struct_1
{
    unsigned int  bit1:10;
    unsigned int  bit2:8;
    unsigned int  bit3:14;
} data1; // sizeof data1 = 4

struct bit_struct_2
{
    unsigned int  bit1:10;
    unsigned int  bit2:8;
    unsigned int  bit3:16;
} data2; // sizeof data2 = 8


/*
 0001 1001 0010 1010 1011 0011 0100 1111 0101 1101 0110 1100 0111 1110 1000 1011
 --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- ---------------------------
    19        2A        B3        4F         5D        6C       8E        9B
    高 <-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·- 低
*/
int main()
{
    unsigned long long ullNum = 0x192AB34F5D6C7E8B;
    memcpy(&data1, (void *)&ullNum, sizeof(data1));
    memcpy(&data2, (void *)&ullNum, sizeof(data2));

    printf("data1 size is %d\n", sizeof(data1));  /* size is 4 */
    printf("[1] bit1 : %u\n", data1.bit1);          /* 651  --> 1010001011 */
    printf("[1] bit2 : %u\n", data1.bit2);          /* 31   --> 00011111 */
    printf("[1] bit3 : %u\n", data1.bit3);          /* 5979 --> 01011101011011 */

    puts("------------------------");
    printf("data2 size is %d\n", sizeof(data2));  /* size is 8 */
    printf("[2] bit1 : %u\n", data2.bit1);          /* 651   --> 1010001011 */
    printf("[2] bit2 : %u\n", data2.bit2);          /* 31    --> 00011111 */
    printf("[2] bit3 : %u\n", data2.bit3);          /* 45903 --> 1011001101001111 */

    return 0;
}

上述代码中我们定义了一个 8B 的 ullNum,其二进制表示如下图所示:

从低字节到高字节分别分配给 bit1、bit2、bit3 :

参考资料