位域是指信息在存储时,并不需要占用一个完整的字节,而只需占一个或几个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。(摘自百度百科)
一、位域定义与结构定义相仿,其形式为:
struct 位域结构名
{
位域列表
};
其中位域列表的形式为: 类型说明符 位域名:位域长度
例如:
struct bs { int a:8; int b:2; int c:6; };
二、位域的几点说明:
1、一个位域必须存储在同一个存储单元中,不能跨两个单元。如果某存储单元空间中不能容纳下一个位域,则该空间不用,而从下一个存储单元起存放该位段。例如:
struct as { unsigned a:4; unsigned b:29;//从下一单元开始存放 };
所以sizeof(as)=8;
struct bs { unsigned a:4; unsigned b:27; };
所以sizeof(bs)=4;
2、位域的长度不能大于数据类型本身的长度,比如int类型就能超过32位二进位,char类型就不能超过8位二进位。例如:
struct bs { unsigned a:4; unsigned b:33;//出错 };
3、位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct bs { unsigned a:4; unsigned :3;//无名位域,不能使用 unsigned b:5; };
三、位域的对齐(摘自百度百科)
如果结构体中含有位域(bit-field),那么VC中准则是:
1、如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2、如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3、如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式:系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作。
四、举例
#include <iostream> using namespace std; typedef struct A { unsigned a:4; unsigned b:2; unsigned c:20; //当没有占满一个存储单元时,结构体的大小对齐为一个存储单元的大小 unsigned d:7; //前面已经为26,故7+26>33已超过一个存储单元,所以7在一个新的存储单元存放 unsigned e:28;//由于前面的7在一个新的存储单元的开头存放,且28+7>32, 故在另一个新的存储单元存放 unsigned :3;//可以定义无名位段,此例中该无名位段占用3位的空间,该空间将不被使用 }; //由于前面的28在一个新的存储单元的开头存放,且28+3<32, 故不用在另一个新的存储单元存放 //所以最后求出的A的大小是4 + 4 + 4 =12 typedef struct B { unsigned a:4; unsigned b:2; unsigned c:20; unsigned d:6; //unsigned i:1;当多出此行时,该结构体大小由4变为8,因为此行之前正好为32位 }; typedef struct C { unsigned a:4; unsigned b:2; unsigned :0; //定义长度为0的位段时不能指定名字,否则编译不过 unsigned c:1; //定义了0字段后,紧接着的下一个成员从下一个存储单元开始存放; }; //此例子中,c前面那个存储单元中的余下的26位中被0填充了 //所以最后求出的A的大小是4 + 4 = 8 int main() { A a; B b;C c; cout<<"sizeof(a)="<<sizeof(A)<<endl; cout<<"sizeof(b)="<<sizeof(b)<<endl; cout<<"sizeof(c)="<<sizeof(c)<<endl; system("pause"); return 0; }
运行结果: