在一些C语言的开发过程中,可能我们的存储空间并不是那么充裕,鉴于这种的情况下想到了C语言中有那么一个名词出现“位域”,在C语言中称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示;位域的出现就是为了解决一些空间浪费的问题,例如:我们通常会定义一些标志位flag,功能模式的切换,在开发过程中可能使用一个开关量“0”和“1”就能够满足我们的需求。
备注:我们这里使用的开发环境是DEV-C++ V5.11
一、位域
形式:
struct 位域结构名
{
位域列表
}位域名;
其中位域列表的形式为: 类型说明符 位域名:位域长度
定义:
位域的定义和结构体的定义一样,可采用先定义后说明,同时定义说明或者直接说明这三种方式。
struct SYSTEM
{
unsigned char fn_flag0:8; //这里占有8bit
unsigned char fn_flag1:2; //这里占有2bit
unsigned char fn_flag2:6; //6bit
unsigned char fn_flag3:1; //1bit
unsigned char fn_flag4:5; //5bit
unsigned char fn_flag5:2; //2bit
unsigned char fn_flag6:6;
unsigned char fn_flag7:2;
unsigned int fn_flag8:32; //32bit
}sys;
说明:
1.一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。
struct data
{
unsigned char a:1;
unsigned char :0;//这里表示不是用空一个bit
unsigned char c:2;
unsigned char d:9;//error
}da;
注:
1.宽度为0的一个未命名位域强制下一位域对齐到其下一type边界,其中type是该成员的类型;
unsigned char :0 ;
经试验,空域所占大小和 a的类型及 空域的类型 二者皆相关。
即以下四种情况,
a和空域皆为char时,二者共占据1字节;
struct SYSTEM
{
unsigned char fn_flag:1;
unsigned char fn_flag1:1;
unsigned char :0;
unsigned char fn_flag2:6;
unsigned char :0;
}sys;
//sizeof(sys) 结果为1byte
a 为unsigned,空域为unsigned; a 为char,空域为unsigned; a 为unsigned,空域为char;这三种情况,二者共占据4字节。
struct SYSTEM
{
unsigned int fn_flag:1; //占4byte
unsigned char fn_flag1:8; //和空域共用1byte,此处满8bit,空域不填充,此处不满8bit,空域填充使其满足1byte
unsigned char :0;
unsigned char :0;
unsigned char :0; //此处都是和前面的fn_flag1共用1个byte
unsigned int fn_flag2:32; //和空域共用4byte,此处满32bit,空域不填充,此处不满32bit,空域填充使其满足4byte
unsigned char :0;
}sys;
2 . 位域的长度不能大于指定类型固有长度,比如说char的位域长度不能超过8,int的位域长度不能超过32,bool的位域长度不能超过8。
unsigned char d:9; //这里定义错误,
3.位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的
struct A
{
unsigned char a:8;
};
位域的使用及占用空间
位域的使用
位域的使用和结构成员的使用相同,其一般形式为: 位域变量名·位域名 位域允许用各种格式输出。
#include <stdio.h>
#include <stdlib.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
struct SYSTEM
{
unsigned int fn_flag1:1; //4
unsigned char fn_flag2:8; //1
unsigned char :0;
unsigned char fn_flag3:2;
unsigned char :0;
unsigned int fn_flag4:8; //4
unsigned char :0;
}sys;
int main(int argc, char *argv[]) {
printf("%d\n",sizeof(sys));
printf("%d %d %d %d %d \n",sys.fn_flag, sys.fn_flag1,sys.fn_flag2,sys.fn_flag3,sys.fn_flag4);
sys.fn_flag1 = 1;
sys.fn_flag2 = 3;
sys.fn_flag3 = 1;
// fn_flag3 = 4;//error,只占有2bit 此时为0,5的时候为1,6的时候为2,其实已经溢出了,只是是unsigned char类型
printf("%d %d %d %d %d \n",sys.fn_flag, sys.fn_flag1,sys.fn_flag2,sys.fn_flag3,sys.fn_flag4);
system("pause");
return 0;
}
位域的对齐
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;
系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作。