位域详解

mashuo 2023-09-08 20:37:51
Categories: Tags:

1. bit-field的定义

首先推荐看官方文档,往往解释的很清楚。

C/C++中的位域(bit-field)是一种特殊的结构体,允许我们按照bit来对成员进行定义,指定其占用的bit空间。

2. bit-field使用方法

bit-field的数据类型可以为一下四种:int , signed int , unsigned int , enum。举下例说明:

#include <stdio.h>
typedef enum{
    enum1 = -10
}en1;

typedef enum{
    enum2 = 10
}en2;

typedef struct{
    int i : 8;           // -128 ~ 127
    unsigned int u : 8;  // 0 ~ 255
    signed int si : 8;   // -128 ~ 127
    unsigned int u1 : 8;  // 0 ~ 255
}s1;

typedef struct{
    int i : 10;           // -512 ~ 511
    unsigned int u : 10;  // 0 ~ 1023
    signed int si : 10;   // -512 ~ 511
    unsigned int u1 : 2;  // 0 ~ 3

    en1 ie : 8 ;            // -128 ~ 127(这里的enum相当于一个signed int)
    en2 ue : 24 ;           // 这个enum相当于一个unsigned int
}s2;

typedef struct{
    int i : 10;           // -512 ~ 511
    unsigned int u : 10;  // 0 ~ 1023
    signed int si : 10;   // -512 ~ 511
    unsigned int u1 : 2;  // 0 ~ 3

    en1 ie : 8 ;            // -128 ~ 127(这里的enum相当于一个signed int)
    en2 ue : 24 ;           // 这个enum相当于一个unsigned int

    unsigned int u2 : 1;  // 0 或者 1
}s3;

typedef struct{
    int i : 10;           // -512 ~ 511
    unsigned int u : 10;  // 0 ~ 1023
    signed int si : 10;   // -512 ~ 511
    unsigned int u1 : 2;  // 0 ~ 3
    float f;
    double d;
}s4;


int main() {
    s2 s;
    s.u1 = 3;
    s.u1++;
    printf("s1的大小为%lu,s1的对齐方式为%lu\n",sizeof(s1), _Alignof(s1));
    printf("s2的大小为%lu,s2的对齐方式为%lu\n",sizeof(s2), _Alignof(s2));
    printf("s3的大小为%lu,s3的对齐方式为%lu\n",sizeof(s3), _Alignof(s3));
    printf("s4的大小为%lu,s4的对齐方式为%lu\n",sizeof(s4), _Alignof(s4));
    printf("s.u1的值为:%d\n",s.u1);
    printf("s.e的值为:%d",s.ie);
    return 0;
}

输出结果如下:

s1的大小为4,s1的对齐方式为4
s2的大小为8,s2的对齐方式为4
s3的大小为12,s3的对齐方式为4
s4的大小为16,s4的对齐方式为8
s.u1的值为:0
s.e的值为:254

可以看出位域结构的大小的规律:

(1) 对比s2和s3位域结构体:位域字段共享内存区域,当所有位域字段的大小超过32时,结构体的大小会再增加4bytes(这是因为位域变量允许的类型大小均为4bytes);
(2) 通过s1,看出位域变量并不影响结构体的对齐方式 ;
(3) 通过s3,可以看出位域结构体的非位域变量独立对位域结构体的大小和对齐方式产生影响。

3. 位域结构体内存分布

关于位域结构体字段的内存分布,可以见文档

4. 使用建议

一般建议不要在c语言中在将位域变量设置为枚举变量,因为c语言中的枚举类型大小是非固定的,根据其内部的枚举值来确定:

typedef enum{
    enum1 = -10
}en1;

typedef enum{
    enum2 = 10
}en2;

typedef enum{
    enum3 = 0xfffffffff
}en3;

typedef enum{
    enum4 = -0xfffffffff
}en4;

int main() {
    printf("枚举类型en1的大小为:%lu\n",sizeof(en1));
    printf("枚举类型en2的大小为:%lu\n",sizeof(en2));
    printf("枚举类型en3的大小为:%lu\n",sizeof(en3));
    printf("枚举类型en4的大小为:%lu\n",sizeof(en4));
    en1 e1 = 0xffffffff;
    en2 e2 = 0xffffffff;
    en3 e3 = 0xffffffffffffffff;
    en4 e4 = 0xffffffffffffffff;
    printf("e1 = %d, e1 + 1 = %d\n",e1,e1+1);
    printf("e2 = %u, e2 + 1 = %u\n",e2,e2+1);
    printf("e3 = %lu, e3 + 1 = %lu\n",e3,e3+1);
    printf("e4 = %ld, e4 + 1 = %ld\n",e4,e4+1);
    return 0;
}

输出结果如下:

枚举类型en1的大小为:4
枚举类型en2的大小为:4
枚举类型en3的大小为:8
枚举类型en4的大小为:8
e1 = -1, e1 + 1 = 0
e2 = 4294967295, e2 + 1 = 0
e3 = 18446744073709551615, e3 + 1 = 0
e4 = -1, e4 + 1 = 0

也就是说,如果枚举变量的值发生变化,那么枚举的大小也很可能发生变化,会对位域结构体的大小产生不确定影响,因此不建议使用。

在c++中,可以显式指定枚举结构体的大小,且枚举类型的值只能为枚举变量,因此可以放心使用。