在实际接触的C语言项目中,我们会发现很多时候都用到了位域。初次听到时可能有些模糊,但其实是很好理解的,以下是经过参考各类资料和大牛的说法后本人对位域的一些理解。
如有问题,欢迎告知,感谢之至。
1.什么是位域?
简单的理解,就是“按位存储”。
实际在c语言中,不同的编译器下,我们对各种类型的变量进行不同字节的存储。百度百科对字节是这么说明的:最常用的字节是八位的字节,即它包含八位的二进制数。(有兴趣的小伙伴们可以自行了解)。这里可简单理解为基本上每个字节都是占8位。
但是有时候我们所需要使用的位数并不需要那么多,比如:开关变量我们设置开为1,关为0,其实只要用一位二进制位就能够实现。因此为了节约存储空间和使处理起来方便。我们便把一个字节分开来,形成不同大小的位域,每个位域有一个域名。这样就可以实现将不同的对象用一个字节来存储。
2.怎么定义位域?
位域的定义类似于结构体的定义(好几种,可参考结构体的定义及声明)。
结构体的一种定义形式为:
struct 结构体名
{
结构体成员列表;
};
类似于此,位域也可定义为:
struct 位域名
{
位域列表;
};
位域列表的定义形式为:
类型说明符 位域名:位域长度;
需要注意的是,位域名后面用“:”(冒号)
3.一个字节中各个位域的位置顺序?
即我们在同一个字节里定义位域的时候,是按照从低到高位,还是从高到低位来存储的呢?我们来做一个实验。
#include<stdio.h>
union o{
struct{
unsigned char a1:2;
unsigned char a2:2;
unsigned char a3:2;
unsigned char a4:2;
}in;
unsigned char test;
}out;
int main(){
out.test=228; //二进制为11100100
printf("a1:%d\n",out.in.a1);
printf("a2:%d\n",out.in.a2);
printf("a3:%d\n",out.in.a3);
printf("a4:%d\n",out.in.a4);
printf("size:%d\n",sizeof(out));
return 0;
}
在联合体out内定义结构体in和变量test,使他们在同样内存里存储。设置无符号char型变量test值为228,其二进制数为11100100,然后分别输出:
发现对应关系如下:
进制 | a4 | a3 | a2 | a1 |
---|---|---|---|---|
十进制 | 3 | 2 | 1 | 0 |
二进制 | 11 | 10 | 01 | 00 |
也就是说,按照我们定义的顺序,再同一个字节里面,是从低位到高位开始存储的。1
4.跨字节位域的存储?
在当前字节所含位数少于我们所需要的位数时,又是怎么存储的呢?我们先来看两段段代码:
#include<stdio.h>
struct ab{
unsigned char a:2;
unsigned char b:4;
}aa;
;
int main(){
printf("%d\n",sizeof(aa));
return 0;
}
这是我们在同一个字节里未超出一个字节时,输出是:
#include<stdio.h>
struct ab{
unsigned char a:2;
unsigned char b:4;
unsigned char c:4;
}aa;
;
int main(){
printf("%d\n",sizeof(aa));
return 0;
}
这是超过了一个字节所含位数时,输出是:
可以发现,当当前的字节存不下时,会从下一个字节开始。且字节大小为位域中最长字节的整数倍。
为了更好理解,当我们把代码改成如下时:
#include<stdio.h>
struct aa{
unsigned char a:2;
unsigned char b:4;
unsigned int c:4;
}aa;
int main(){
printf("%d\n",sizeof(aa));
return 0;
}
会出现这样的结果:
这是为什么呢?其实,与结构体类似,位域也需要遵循内存对齐的原则。我们首先考虑类型所占用的字节,总的字节大小是最大对齐数的整数倍,所以是4的整数倍。然后才考虑实际每个字节里面具体位域分布(关于结构体的内存对齐感兴趣的可以自行了解)。