作为C语言新手,只能凭自己看的一些资料谈一谈自己的理解,错误之处还望不吝指正。
位域(位段)
位域是指信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
—–摘自《百度百科》
1、位域的定义
位域的定义和位域变量与结构体的定义类型,其结构为:
struct 位域结构名{
位域类型 位域名:位域长度;
位域类型 :位域长度;
...
};
在K&R中,规定的位域类型只能是ints,应该是包括了signed和unsigned的char、short和int。而且最好注明是signed还是unsigned类型。虽然在C99中增加了新的支持类型,不过为了兼容性,还是遵循以前的标准吧。
我觉得,位域只是长度缩小了的位域类型,能对位域类型进行的操作,对位域也可以。比如对int型的位域,也可以用printf("%d", 位域名)
来输出。
位域变量的说明和结构体变量的说明方式相同,可采用先定义后说明,同时定义说明或者直接说明三种方式,例如:
struct bf{
int a:8;
int b:2;
int c:6;
}test;
说明test是bf的变量,共占两个字节。其中位域a占8位,b占2位,c占6位。
2、位域压缩与对齐
1.压缩:如果相邻位域字段的位域类型相同,各个位域字段只占定义时的bit长度。
struct bf{
char a : 2;
char b : 3;
char c : 1;
}test;
其大小为 1 bytes,a、b、c在一个char中。
2.压缩和字段对齐:一个位域必须储存在同一个字节中,不能跨两个字节。如果一个字节剩余的空间不够存放另一个位域的时候,应该从下一个单元起存放该位域。剩余空间补0例如:
struct bf{
char a : 2;
char b : 3;
char c : 7; //从下一单元开始存放
}test;
test.a = 2;
test.b = 2;
test.c = 2;
其大小为2 bytes,a和b在一个char中,c在第二个char中
%d输出test可以发现,其结果为522,那么这是为什么呢,我们用一个图来表达:
本来位域C应该是继续往前放的,但是由于一个单元(字节)只有8位,不够了,所以它只能放到第二个单元(字节)里,而第一个单元的剩余位用0补齐,所以最后的答案就是522了。
3.位域类型对齐:如果相邻的位域字段的类型不同,在不同的位域类型间,按通用的对齐规则进行不同数据类型间的对齐(注意,struct的长度是其内部最宽类型的整数倍);同时在相同位域类型的字段间按以上两条规则对齐。
struct bf{
char a : 1;
char : 2;
long b : 3;
char c : 2;
}test;
其大小为 12 bytes。
因为结构体的对齐规则中,其中如果是上图这种情况,那么最长数据类型为long,占4个字节,其在内存空间存放方式如下:
|–char a–|—-|—-|—-| 4字节
|———-long———-| 4字节
|–char c–|—-|—-|—-| 4字节
4.终止压缩:如果位域字段中间穿插着非位域字段,则不进行压缩。
struct bf{
char a : 2;
long a1;
char b : 3;
long b1;
char c : 7;
}test;
其大小为 20 bytes,如果注释掉//long a1;则为 12 bytes。
5.位域可以无位域名:这时它只是来作填充或者调整位置的。无名的位域是不能使用的。例如:
struct bf{
int a : 4;
int : 2; //无位域名,该2位不能使用,只是调整位域b的位置
int b : 4;
}
3、总结和问题
1.在查阅资料的过程中,发现有很多的都说,位域的长度是不能跨字节的,我个人认为这个是不对的,在实际的代码中就可以看出问题,比如:
#include<stdio.h>
struct bf{
int a : 2;
int b : 3;
int c : 18;
}test;
int main(){
test.a = 2;
test.b = 2;
test.c = 50 * 1024;
printf("%d\n", test.c);
printf("%d\n", test);
return 0;
}
输出的test.c是40960,test是1310730,可以看出c是可以取到18bits的。
2.因为在一个byte中,也是要分big-endian和little-endian的,所以位域也不是那么地好用。
3.题目实战
#include<stdio.h>
struct mybitfields
{
unsigned short a : 4;
unsigned short b : 5;
unsigned short c : 7;
} test;
int main()
{
int i;
test.a = 2;
test.b = 3;
test.c = 0;
i = *((short *)&test);
printf("%d\n", i);
return 0;
}
那么这道题的答案就是50,至于为什么,就留给大家自己思考了,哈哈哈哈哈哈!