引言
对位段进行介绍,什么是位段,位段如何节省空间,位段的内存分布,位段存在的跨平台问题,及位段的应用。
✨猪巴戒:个人主页✨
所属专栏:《C语言进阶》
跟着猪巴戒,一起学习C语言
目录
引言
什么是位段?
位段的作用
位段是如何节省空间?位段的内存分配
位段的内存分配
位段的跨平台问题
位段的应用
什么是位段?
位段的声明和结构是类似的,有两个不同:
- 位段的成员必须是整形家族的类型
- 位段的成员名后面有一个冒号和一个数字。
冒号后面的数字表示的是这个变量将使用多大的内存(单位是比特位)。
int _n : 2表示的是_n这个整形变量本来是4个字节(也就是32个比特位),但是现在要将它的内存缩减到2个比特位。
struct A{int _n : 2;int _b : 5;int _c : 10;int _d : 30;};
整形家族的介绍:
整形类型不仅包括基本整形,还有短整形,长整形,双长整形,字符型和布尔型。
类型 | 字节数 | 取值范围 |
int(整形) | 4 | -2147483648~2147483647 |
short(短整型) | 2 | -32768~32767 |
long(长整型) | 4/8 | -2^31~(2^31-1) |
long long(双长型) | 8 | -2^63~(2^63-1) |
char | 1 | -2^7~(2^7-1) |
bool | 1 | 1,0 |
位段的作用
位段的作用就是节省空间。当我们有一些成员的取值范围有限的时候,所需要的内存空间也不需要太多的空间。
比如说布尔类型
只需要表示真或者假,1表示真,0表示假。实际上布尔类型的变量只占有1个比特位,(1个字节代表着8个比特位),如果使用位段就可以帮助我们节省大量的空间。
由此,位段所执行的大小不能大于这个变量类型本身的大小,不然就会报错。
位段是如何节省空间?位段的内存分配
下面是刚才给大家做示范的例子,我们现在看这个位段所占用的空间:
#include struct A{int _n : 2;int _b : 5;int _c : 10;int _d : 30;};int main(){printf("%d\n", sizeof(struct A));return 0;}
_n,_b,_c,_d这四个变量的大小加在一起,一共是47个比特位,那就是需要6个字节的大小。
但是,我们发现struct A的大小不仅仅是6个字节,而是8个字节。
这就说明位段中内存分布不仅仅是简单组合,顺序地一个紧挨着一个地存放。
位段的内存分配
- 位段的成员可以是整形家族的类型
- 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
- 位段涉及很多不确定因素,位段是不跨平台的,注意可移植的程序应该避免使用位段
2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
1.例子
下面位段的大小是8个字节
struct A{int _n : 2;int _b : 5;int _c : 10;int _d : 30;};
解析struct A的内存分布:
还是拿这个例子给大家举例:
因为是位段都是int类型,所以这里位段的空间按照4个字节的方式开辟。
首先开辟4个字节来存放内存。
_n是2个比特位,_b是5个比特位,_c是10个比特位,一共是17个比特位
放在4个字节(32个比特位/一个整形大小)当中,剩下15个比特位。
是不足够放下_d(30个比特位),所以另外开辟一个整型大小(4个字节/32个比特位),将_d放进去。
_n,_b,_c放在前面的一个整形,_d放在后面的一个整形。
一共是两个整形,就是8个字节。
2.例子
下面位段的大小是3个字节
#include struct S{char a : 3;char b : 4;char c : 5;char d : 4;};int main(){printf("%d\n", sizeof(struct S));struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;return 0;}
解析struct S位段的内存分布:
这个位段都是char 类型,所以这里位段的空间按照1个字节的方式开辟。
1.首先开辟1个字节(8个比特位),来存放数据
a是3个比特位,b是4个比特位,那么开辟的第一个字节就剩下1个比特位,是不足够存放5个比特位的c变量。
2.开辟下一个字节(8个比特位)存放数据
c是5个比特位,这个字节还剩下3个比特位,不足够存放4个比特位的d变量。
3.开辟下一个空间(8个比特位)存放数据
d是4个比特位,将d存放第三个字节中。
解析struct S的数据在内存中的具体分布:
//在位段中的成员被赋值后,观察内存分布struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;
struct S在内存中的分布其实是有所差异的,当位段中的成员被赋值后,我们就可以看到位段中的成员是如何分布的。
首先开辟一个字节后,
a=10的二进制数字是01010,位段:3个比特位,所以a的值放入内存中会被裁断,变成010.
b的值是12,位段:4个比特位,所以b存到内存中的值是1100.(数据是以二进制的形式存到内存)
a,b的存放方式如下图:a和b在第一个字节中的排列方式是从右到左的,也就是从高地址向低地址,在一个字节中优先排放高地址。
第二个字节开辟,
c=3的二进制数字是11,位段:5个字节,不足5个字节用0来补充高位,也就是00011.
c的存放方式也是在这个字节中优先排放高地址处。
第三个字节开辟,
d的值是4,二进制数字是100,位段:4个字节,高位用o来补充,也就是0100
d的存放方式也是在这个字节中优先排放高地址处。
所以位段struct S在内存中存放的数据是01100010 00000011 00000100
换算成十六进制数字就是 6203 04
验证:
通过内存调试,观察内存窗口就可以看到
位段的跨平台问题
- int 位段被当成有符号数还是⽆符号数是不确定的。
- 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会出问题。
- 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
- 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利⽤,这是不确定的。
总结:
跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。
位段的应用
下图是⽹络协议中,IP数据包的格式,我们可以看到其中很多的属性只需要⼏个bit位就能描述,这⾥使⽤位段,能够实现想要的效果,也节省了空间,这样⽹络传输的数据报⼤⼩也会较⼩⼀些,对⽹络的畅通是有帮助的。大量的数组经过位段的处理,可以将数据的内存缩小,是数据包的大小变小,数据包变小,网络的传输就会变得快速。