关于位段,首先要知道什么是位?

位段是为了给一个数据类型重新定义所占的内存空间(节省空间),那么位,也就是所谓的二进制位(bit位);

一.声明:

位段的声明和结构体类似,但是位段的成员必须是int,unsigned int ,signed int ,或char(属于整型)。

struct A //位段的成员名后边有一个冒号和一个数字
{
 int _a:2;  //2表示_a占2个bit位
 int _b:5; //5表示_b占5个bit位
 int _c:10; //...
 int _d:30;//...
};

A就是一个位段类型 ; 这样之后,原本占4个字节的_a,_b,_c,_d ,大小就被就被重新定义;


在C语言中,位段(Bit - field)成员要求是整型的原因:

位段是一种对字节中的位进行操作的数据结构方式。整型数据类型(如int、unsigned int等)在内存中的存储是以二进制位的形式存在的,这使得它能够方便地按照位进行处理。计算机处理数据的基本单位是字节,字节又是由二进制位组成的,整型和内存存储的基本单位(位)在概念上是相匹配的

而其他非整型数据类型(如浮点数),其存储格式和位的解释方式比较复杂,不适合用于位段。浮点数在内存中的存储涉及符号位、指数位和尾数位等,这种存储方式是为了表示带有小数部分的数值,无法简单地像整型一样进行位操作来分割出位段

二.位段的内存分配

在了位段的内存分配之前,先来思考:

struct A //位段的成员名后边有一个冒号和一个数字
{
 int _a:2;  //2表示_a占2个bit位
 int _b:5; //5表示_b占5个bit位
 int _c:10; //...
 int _d:30;//...
};

位段A的大小是多少?

按理说,2+5+10+30 = 47个bit位,1个字节是8个bit位,那么开辟6个字节的空间储存数据肯定是足够的,但是输出却发现 :

##位段_位段

A的大小为8个字节,由此可以说明一个概念:

位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。

以int为例,位段储存数据时,先一次性开辟四个字节,等到空间不够用时再一次性开辟四个字节。##位段_整型_02

但是d是从c后面的空间开始储存还是从新开辟4字节的空间开始存储是不确定的。

三.数据的存储:

那么对类型大小重新定义之后,数据是怎样储存的呢?

struct A //位段的成员名后边有一个冒号和一个数字
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};
int main()
{
	printf("%d\n", sizeof(struct A));
	struct A s;
	s.a = 5;
	s.b = 3;
	s.c = 10;
	s.d = 2;
	return 0;
}

以这一组数据为例 :(每个字节内从右往左存数据)##位段_存储方式_03

如果按照这种储存方式,8个字节中中分别储存的是这些数据,每个字节在地址中储存的值应该为: 0d 05 (00 00未知) 02 00 00 (00)(地址为16进制数,四个bit位表示一个16进制数);

验证:

##位段_数据_04

在VS2022编译环境下,为上述存储方式,且结果和我们预测的数值相同,这就是使用位段之后数据的存储方式;

四.跨平台问题:

  • int 位段被当成有符号数还是无符号数是不确定的。
  • 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
  • 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  • 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

所以,在不同机器,平台下,每一次段位都需要重新去写,即使跟结构相比,位段在达到到同样的效果同时,很好的节省了空间,但是跨平台的问题存在,让位段在使用时变得不是那么方便。

五.位段应用:

单片机,底层网路(提高性能)

可能需要很多整型,但是每个整型实际需要的空间很小,这时就可以使用位段。