位结构体和位域
1. 位域的定义
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:
位结构定义的一般形式为:
struct 位结构名{
数据类型 [变量名]: 整型常数; //成员称为“位域”或者“位段”
数据类型 [变量名]: 整型常数;
} 位结构变量;
其中: 数据类型必须是整型(包括字符型)。 整型常数的范围是数据类型的长度, 如定义为short,则范围是1~16,也就是说,当定义一个short类型的成员位段时,它的整形常数取值范围必须在0-16之间,超出范围编译器将报语法错。具体范围如下:
类型 | 整形常数范围 |
char, unsigned char | 1-8 |
short, unsigned short | 1-16 |
int, unsigned | 1-32 |
long, unsigned long | 1-32 |
当然上面的范围值也可以是0,但是作用不同,后面会提到。
例如:
struct webpage{
unsigned char incon: 8; // incon占用低字节的0~7共8位
unsigned char txcolor: 4; // txcolor占用高字节的0~3位共4位
unsigned char bgcolor: 3; // bgcolor占用高字节的4~6位共3位
unsigned char blink: 1; // blink占用高字节的第7位
}ch;
printf("%d\n", sizeof(struct webpage));
输出结果:2。
2. 位域的对齐与计算
上面结构体大小为2,但是,为什么会是2呢?先来了解下,结构体的对齐原则:
1). 若相邻成员变量类型相同,且其位宽之和不大于成员变量类型位宽(在此严重强调,是类型位宽而不是成员变量sizeof,也不是类型的sizeof或者其他什么)大小,则
后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2). 如果相邻位域字段的类型相同,但其位宽之和不大于成员变量的类型宽度大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3). 如果相邻的位域字段的类型不同,不同位域字段存放在不同的位域类型字节中(这一条其实会根据编译器的不同而采用不同的规则)。
例如:
struct A
{
char c1:1;
char c2:3;
unsigned short s2:13;
unsigned long i:3;
};
printf(“%d\n”, sizeof(struct A));
我们先分析下,如果遵循上面的对齐原则,结果应该会是多少。
c1, c2符合原则2,类型相同,那么就是 c1占1位,在0上;c2占3位在1~3上,因为后面的unsigned short与c1, c2不是同类型,符合原则3,所以存储在新的字节上。即c1, c2在一个字节上; s2占13位,在0-12上,也因和后面的i不是同类型,所以存储在新字节上,即s2占两个字节。i占3位,在0-2上,占四个字节。
即:1 + 2 + 4 = 7,但实际上,结构体还有个原则要遵守,即:和结构体一样,位结构体也是按照成员的最大长度字节来对齐分配空间的。如顺序读到的位域中最长的那个字节数对齐,如为4字节的long,则按4字节对齐。若最长仅为char,则按char对齐。即依次按读取到的数据类型进行对齐,并计算出最后的sizeof()!所以,这里最大的成员类型是4个字节,结构体的大小会是它的倍数,所以为距7最小4的倍数是8。得出,上面位结构体大小为:8,VC 6.0下输出结果也为8。
但是,在Code::Blocks 10.5中,输出结果却为4。也就是说,它并没有遵循上面的原则3。所以,原则3会和具体的编译环境有关。
如果位域上的整形范围值是0,则下个位域从新的字节开始(即使是同类型的位域),如:
struct bs
{
unsigned a:4;
unsigned :0; // 空域
unsigned b:4; // 从新字节开始存放
unsigned c:4;
} ;
上面这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从新的字节开始,占用4位,c占用4位。上面位结构体大小为:8
3. 位域的访问
位结构成员的访问与结构成员的访问相同。 例如: 访问下例位结构中的bgcolor成员可写成ch.bgcolor 进行访问:
struct webpage{
unsigned char incon: 8; // incon占用低字节的0~7共8位
unsigned char txcolor: 4; // txcolor占用高字节的0~3位共4位
unsigned char bgcolor: 3; // bgcolor占用高字节的4~6位共3位
unsigned char blink: 1; // blink占用高字节的第7位
}ch;
位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct k
{
int a:1;
int :2; // 该2位不能使用
int b:3;
int c:2;
};
注意:
1. 一个位域必须存储在定义它的一个数据类型内部,不能跨跨该数据类型。如char定义的位域所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。
2.由于位域不允许越过定义它的数据类型,因此位域的长度不能大于定义它的数据类型的长度。
3. 位结构总长度(位数), 是各个位成员定义的位数之和再向最大结构成员对齐。
4. 位结构成员可以与其它结构成员一起使用。
例如:
struct info{
char name[8];
int age;
struct addr address;
float pay;
unsigned char state: 1;
unsigned char pay: 1;
}workers;
上例的结构定义了关于一个工人的信息。其中有两个位结构成员, 每个位结构成员只有一位, 用unsigned char数据类型,因此只占一个字节但保存了两个信息, 该字节中第一位表示工人的状态, 第二位表示工资是否已发放。由此可见使用位结构可以节省存贮空间。
以上参考:http://blog.csdn.net/juliababy/archive/2008/09/14/2873796.aspx,http://www.svn8.com/c/c/2010040928725.html