C语言中一个位域结构占多少个字节呢

2022-11-17
关注

在c语言中提供一个一种叫 “位域” 或者 “位段” 的数据结构。它的存在是为了更加的节省空间。因为在有些实际需求中,并不需要占用一个完整的字节,而只是需要一个或者几个二进制位。比如存在一个开关量时,只有 0 和 1 两种状态,只需要一个二进制位存储即可。

位域的定义如下:

struct 位域结构名{ 位域列表 };

其中位域列表的形式为:类型说明符 位域名:位域长度

例如,

struct test{
   int a:3; 
   int b:2; 
   int c:6;
};

位域的变量的说明 与结构体的方式一样,有三种方式:先定义后说明、同时定义说明、直接说明。

在声明时,位段成员必须是整形或枚举类型(通常是无符号类型)。

结构中也可以包含无名位域,作为相邻成员之间的填充或调整位置。无名位域无法被引用,它们的内容在运行时是不可预测的。

struct s{
   unsigned int a:4; //a存放结构的前4个位中 
   unsigned int :2;  // a后面的2个位填充
   unsigned int b:2;  
};

位域的定义有如下限制

1、一个位域必须存储在同一个字节中,不能跨两个字节。

比如当一个字节所剩的空间不够下一个位域存储时,则从下一个存储单元的起始位置开始存放;也可以专门让某个位域从下一个存储单元的起始位置开始。

struct s{
   unsigned int a:4; //a存放结构的前4个位中 
   unsigned int :2;  // a后面的2个位填充
   unsigned int b:6;  // 剩余的2位不够存储,从下一个存储单元开始存放
   unsigned int :0; //空位域,把该unsigned int剩余空间自动全部填充0
   unsigned int c:5; //从下一个单元存储,也即是从下一个 unsigned int 开始

}ss;

在这个位域中,a 占第一个字节的前 4 位,a 后面 2 位进行填充,第一个字节剩余的 2 位不够 b 存储,因此 b 从下一个字节开始存储,b 后面的位域填充 0,c 从下一个单元存储,该下一个单元指的是下一个 unsigned int,因为空位域 0 会把 b 后面 unsigned int 所剩的空间全部填充 0, 因此 sizeof(ss) = 8。

2、位域可以是无名域,无名域就是类似于 unsigned int : 0; 或者 unsigned int : 2; 虽然两者只是 0 和非 0 的区别,但是作用却是不同的。若无名域的位数为 0,则下一个位域将会强制从下一个单元开始(这里的一个单元指的不是下一个字节,而是跨过跨过一次数据类型的自然边界);如果无名位域为非 0,则意味着这个无名位域占着空间,不能被使用。

struct test{
   unsigned int a:3;  //a占3位
   unsigned int :0;  //对于unsigned int 类型 a 后面的剩余未全填充0
   unsigned int b:2;  //b 从下一个unsigned int 类型开始
}tt;

在该例子中 a 占 unsigned int类型所占字节的3位,a 后面的无名域会把 a 后面的所有位填充 0,因此对于 unsigned int 类型所占的 4 字节空间,除 a 的 3 位外,其他位均为0,b 从下一个 unsigned int 类型开始,所以 sizeof(tt) 为 8。

struct test{
   unsigned int a:3;  //a占3位
   unsigned int :2;  //a后面的2个位填充
   unsigned int b:2;  // b 跟着无名域后面
}tt;

a后面无名域占 2 位,b跟在无名域后面,所以 a 和 b共占用一个字节。因此sizeof(tt) 为 4。

3、如果相邻位域字段的类型相同,且其位宽之和小于类型的 sizeof 大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止 。****

struct  test {
    unsigned char a:3;
    unsigned char b:4;
} t;

a 和 b 所占的宽度之和小于 sizeof(unsigned char)大小,因此 a和b共同使用一个字节,所以 sizeof(t) 为 1;

4、如果相邻位域字段的类型相同,但其位宽之和大于类型的 sizeof 大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍 。****

struct  test {
    unsigned int a:15;
    unsigned int b:20; //从下一个unsigned int开始
} t;

由于 a 和 b 宽度之和大于 sizeof(unsigned int)大小,因此b从下一个 unsigned int 处开始,所以 sizeof(t) 为 8。

5、整个结构体的总大小为最宽基本类型成员大小的整数倍。

struct  test {
    unsigned char a : 3;
    unsigned char b : 4;
    unsigned char c : 3;
    unsigned int  d : 5;
} t;

整个结构体的总大小为 unsigned int 类型的整数倍。

6、如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式。(跟编译器有较大的关系,使用时要慎重,尽量避免)

struct  test {
    unsigned char a : 3;
    unsigned char b : 4;
    unsigned char c : 3;
    unsigned int  d : 5;
} t;

对于该类型,不压缩时,a和b类型相同会占用一个字节,c会占用一个字节,d会单独存放,不会和共占用一个字节,因为整个结构体的总大小为最宽基本类型成员大小的整数倍,因此 sizefo(t)为 8。

若压缩时(gcc下),c 和 d 共占用一个字节,因为整个结构体的总大小为最宽基本类型成员大小的整数倍,因此 sizefo(t)为 4。

7、 如果位域字段之间穿插着非位域字段,则不进行压缩;(不针对所有的编译器,跟编译器有较大的关系,使用时要慎重,尽量避免)

struct test{
    unsigned int m: 12;
    unsigned int ch;
    unsigned int p: 4;
}t;

sizeof(t) 大小为 12。

8、当使用有符号类型来定义位域,并且无意中使用到了正负(有意或者无意)特性时,可能出现不是想要的结果

struct test
{
    char a : 2;
    char b : 3;
    char c : 3;
};

struct test t;

// 位域赋值
t.a = 0x3;   // 11
t.b = 0x5;   // 101
t.c = 0x2;   // 010

printf("%d,%d,%d\\n", t.a, t.b, t.c); //结果为 -1, -3, 2

可见,当为域的最高位是 1 的时候,会进行符号扩展,而且这也取决于编译器的实现,因此,为避免此类问题,最好使用无符号类型定义位域。

9、取地址操作符 & 不能应用在位域字段上,因此不存在位域的指针。

10、位域字段不能是静态成员。

11、不能用来指定位数的类型。

若 struct 成员是指针变量类型不能用来指定所占的位数,在 64 位系统中指针固定占 8 字节,在 32 位系统中指针固定占 4 字节。

若 struct 成员是 double 或 float 类型,不能指定位数,否则编译出错,位域类型无效。

审核编辑:刘清

您觉得本篇内容如何
评分

评论

您需要登录才可以回复|注册

提交评论

大怪科学

这家伙很懒,什么描述也没留下

关注

点击进入下一篇

test

提取码
复制提取码
点击跳转至百度网盘