位结构体和位域

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.aspxhttp://www.svn8.com/c/c/2010040928725.html

posted @ 2010-10-09 00:36  jeff_nie  阅读(2124)  评论(0编辑  收藏  举报