关于位段,首先要知道什么是位?
位段是为了给一个数据类型重新定义所占的内存空间(节省空间),那么位,也就是所谓的二进制位(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为例,位段储存数据时,先一次性开辟四个字节,等到空间不够用时再一次性开辟四个字节。
但是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;
}
以这一组数据为例 :(每个字节内从右往左存数据)
如果按照这种储存方式,8个字节中中分别储存的是这些数据,每个字节在地址中储存的值应该为: 0d 05 (00 00未知) 02 00 00 (00)(地址为16进制数,四个bit位表示一个16进制数);
验证:
在VS2022编译环境下,为上述存储方式,且结果和我们预测的数值相同,这就是使用位段之后数据的存储方式;
四.跨平台问题:
- int 位段被当成有符号数还是无符号数是不确定的。
- 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
- 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
所以,在不同机器,平台下,每一次段位都需要重新去写,即使跟结构相比,位段在达到到同样的效果同时,很好的节省了空间,但是跨平台的问题存在,让位段在使用时变得不是那么方便。
五.位段应用:
单片机,底层网路(提高性能)
可能需要很多整型,但是每个整型实际需要的空间很小,这时就可以使用位段。