位断
什么是位段
位段的声明和结构体是类似的,有两个不同:
1.位段的成员必须是int、unsigned int 或 signed int (也有一些有char)
2.位段的成员后面有一个冒号和一个数字。
例如:
struct A{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
其中A就是一个简单的位段。
我们还是先算一下这个位段占多少个字节。
struct A{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
int main(){
struct A a;
printf("%d\n", sizeof(a));
return 0;
}
结果是:
为什么是8呢?这里发现跟结构体的大小计算不太一样了。
注意名字位段中的位,这个位指的是二进制的位。一个字节有8位。按照这样来算 我们把成员变量中的abcd后面的各自的位数加起来 :
2+5+10+30=47。如果是47 那我们只需要6个字节就够了呀,6X8=48>47了 为什么答案是8呢?
这就需要我们了解接下来的位断的内存分配了。
位段的内存分配
规则:
1.位段的成员可以是int unsigned int signed int 或者是char类型。
2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的。
3.位断设计很多不确定因素,位断是不跨平台的,注重可移植的程序应该避免使用位段。
那么根据以上三个规则我们来分析一下位段A
因为成员变量均为int类型,所以开辟空间是以4个字节开辟的。那么起初先开辟4个字节,来存储a、b、c,但是存储d的时候发现空间不够,就继续开辟四个字节32位来存储d,d前面的空间就浪费掉了。最后其实是在内存中开辟了8个字节。所以求的结果为8。
既然我们看到了规则中开辟空间最大以4个字节去开辟,那也就是说成员变量后面带的数字位数是不能够大于32的。
如果大于32,在编译的时候是会报错的。
总的来说位段的存在很大的一个作用就是节省空间
看完了int类型的成员变量,我们再来看一下char类型的成员变量。
struct S{
char a:3;
char b:4;
char c:5;
char d:4;
};
int main(){
struct S s;
printf("%d\n",sizeof s);
return 0;
}
那么我们可以根据上面计算规则先算一下这个位段s的大小。算出来结果为:3
每次先开辟1个字节最后3个字节才能够存储这个位段。
接下来我们详细讨论一下位段的存储以及赋值。
struct S{
char a:3;
char b:4;
char c:5;
char d:4;
};
int main(){
struct S s={0};
s.a=10;
s.b=20;
s.c=3;
s.d=4;
// printf("%d\n",sizeof s);
printf("%d\n",s.a);//2
printf("%d\n",s.b);//4
printf("%d\n",s.c);//3
printf("%d\n",s.d);//4
return 0;
}
先将这个位段初始化为0;然后分别赋值
a赋值为10 10转换为2进制为1010 但是a只在内存中包含3个bit位
只存后面三个位置010
b赋值为20 20转换为2进制为10100 但是a只在内存中包含4个bit位
只存后面四个位置0100
c赋值为3 3转换为2进制为011 c在内存中包含5个bit位 所以可以全部存储,但是前面还要补全两个0
d赋值为4 4转换为2进制为100 d在内存中包含4个bit位 所以可以全部存储
但是前面要补全一个0
最后的存储如下:
所以在内存中你能看到的三个字节为
22 03 04
编译器的输出结果位:
内存视图如下:
跟我们计算的结果一致。
位段的跨平台问题
1.int位段被当成有符号数还是无符号数是不确定的。
2.位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32)
3.位段中的成员在内存中从左向右分配。还是从右向左分配标准尚未定义。。
4.当一个结构体包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是利用,这个是不确定的。
总结
跟结构体相比,位段可以达到同样效果的同时节省空间,但是跨平台的问题依然存在。