1、位域简介
在嵌入式编程中,经常会遇到下面的结构:
- struct _data
- {
- char a:6;
- char b:2;
- char c:7;
- }data;
- struct _data
- {
- char a:6;
- char b:2;
- char c:7;
- }data;
在存储信息的时候,我们可能并不需要占用一个完整的字节,而只需占一个或几个二进制位,如要存储一个八进制数据,只需要3 个二进制位就够了。为了节省存储空间,C 语言提供了位域这种数据结构。所谓位域,就是把存储空间中的二进制位划分为几个不同的区域,并说明每个区域的位数,每个域有一个域名,允许在程序中按域名进行操作。
2、位域的定义及变量的说明
定义位域的一般形式如下:
- struct 位域结构名{
- 类型说明符 位域名a:位域长度;
- ……
- 类型说明符 位域名b:位域长度;
- };
位域变量的说明:
以开头提到结构体来说明:data 为struct _data 类型变量。占有2个字节,其中a:占6位,b占2位,c占7位;
3、位域定义的几点说明
1) 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也 可以有意使某位域从下一单元开始。
2) 位域的长度不能大于指定类型固有长度,比如说int的位域长度不能超过32,bool的位域长度不能超过8。
3) 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
- struct data_ {
- int a:1
- int :2 /*该2位不能使用*/
- int b:3
- int c:2
- }data;
4、位域使用及存储空间说明
1)位域的使用:
- struct 位域结构名{
- 类型说明符 位域名:位域长度;
- ……
- 类型说明符 位域名:位域长度;
- } 位域变量名1,位域变量名2……;
- /*其中,位域结构名可以省略掉,直接定义位域变量,如:*/
- struct {
- 类型说明符 位域名:位域长度;
- ……
- 类型说明符 位域名:位域长度;
- } 位域变量名1,位域变量名2……;
- /*还可以先定义位域类型,再定义位域变量名,如:*/
- struct 位域结构名{
- 类型说明符 位域名:位域长度;
- ……
- 类型说明符 位域名:位域长度;
- };
- struct 位域结构名 位域变量名1,位域变量名2……;
2) 位域存储空间说明
示例1:
- #include <stdio.h>
- struct data_ {
- char a : 5;
- char b : 3;
- char c : 7;
- }data;
- int main()
- {
- printf("data 的起始地址是:%p\n",&data);
- printf("data 的占有的字节数:%d\n",sizeof(struct data_));
- }
- james@jsh:~$ ./a.out
- data 的起始地址是:0x804a01c
- data 的占有的字节数:2
示例2:
- #include <stdio.h>
- struct data_ {
- char a : 5;
- char b : 6;
- char c : 7;
- }data;
- int main()
- {
- printf("data 的起始地址是:%p\n",&data);
- printf("data 的占有的字节数:%d\n",sizeof(struct data_));
- }
- james@jsh:~$ ./a.out
- data 的起始地址是:0x804a01c
- data 的占有的字节数:3
通过上面的2个例子,我们可以充分说了,上节中的第一条:
一个位域必须存储在同一个字节中,不能跨两个字节。
示例 3:
- #include <stdio.h>
- struct data_ {
- char a : 6;
- int b : 22;
- char c : 7;
- }data;
- int main()
- {
- printf("data 的起始地址是:%p\n",&data);
- printf("data 的占有的字节数:%d\n",sizeof(struct data_));
- }
- james@jsh:~$ ./a.out <span style="color:#ff0000;">
- </span>data 的起始地址是:0x804a01c
- data 的占有的字节数:8<span style="color:#ff0000;">
- </span>
- #include <stdio.h>
- struct data_ {
- char a : 6;
- int b : 30;
- char c : 7;
- }data;
- int main()
- {
- printf("data 的起始地址是:%p\n",&data);
- printf("data 的占有的字节数:%d\n",sizeof(struct data_));
- }
- james@jsh:~$ ./a.out
- data 的起始地址是:0x804a01c
- data 的占有的字节数:12
示例5:
- #include <stdio.h>
- struct data_ {
- char a : 6;
- int b : 12;
- char c : 7;
- }data;
- int main()
- {
- printf("data 的起始地址是:%p\n",&data);
- printf("data 的占有的字节数:%d\n",sizeof(struct data_));
- }
- james@jsh:~$ ./a.out
- data 的起始地址是:0x804a01c
- data 的占有的字节数:4
上述测试:必须对内存对齐方式有一定的了解,简单说明下吧!内存对齐应遵循下来3个规则:
1、按字节对齐 2、按字对齐 3、按半字对齐 (一个字是4个字节,半字2个字节)
位域的存储首先应该遵循内存的对齐放弃,再遵循上述3节第一条部分。
再补充点就是:结构体成员排列最好的方式,按类型占有字节的大小,按大到小排列!
- #include <stdio.h>
- #pragma pack (2)
- struct _data
- {
- int a:16;
- unsigned char b:5;
- char c:5;
- }data;
- void main()
- {
- int *p=(int *)&data;
- printf(" 位域结构的起始地址为:%d\n\n",p);
- data.a=2;
- printf(" 整型指针p 所指向的单元存储的值为:%d\n",*p);
- printf(" 位域a 的值为:%d\n",data.a);
-
- char *p1=(char*)(p+1);
- data.b=18;
- printf("\n字符指针p1所指向的单元存储的值为:%d\n",*p1);
- printf(" 位域b 的值为:%d\n",data.b);
- data.c=255;
- char *p2;
- p2 = p1+1;
- printf("\n字符指针p2所指向的单元存储的值为:%d\n",*p2);
- printf(" 位域c 的值为:%d\n",data.c);
- return ;
- }
- 运行结果:
- 位域结构的起始地址为:4233624
- 整型指针p 所指向的单元存储的值为:2
- 位域a 的值为:2
- 字符指针p1所指向的单元存储的值为:18
- 位域b 的值为:18
- 字符指针p2所指向的单元存储的值为:31
- 位域c 的值为:-1。
- #include <stdio.h>
- #pragma pack (2)
- struct _data
- {
- int a:16;
- unsigned char b:5;
- char c:5;
- }data;
- void main()
- {
- int *p=(int *)&data;
- printf(" 位域结构的起始地址为:%d\n\n",p);
- data.a=2;
- printf(" 整型指针p 所指向的单元存储的值为:%d\n",*p);
- printf(" 位域a 的值为:%d\n",data.a);
- char *p1=(char*)(p+1);
- data.b=18;
- printf("\n字符指针p1所指向的单元存储的值为:%d\n",*p1);
- printf(" 位域b 的值为:%d\n",data.b);
- data.c=255;
- char *p2;
- p2 = p1+1;
- printf("\n字符指针p2所指向的单元存储的值为:%d\n",*p2);
- printf(" 位域c 的值为:%d\n",data.c);
- return ;
- }
- 运行结果:
- 位域结构的起始地址为:4233624
- 整型指针p 所指向的单元存储的值为:2
- 位域a 的值为:2
- 字符指针p1所指向的单元存储的值为:18
- 位域b 的值为:18
- 字符指针p2所指向的单元存储的值为:31
- 位域c 的值为:-1。