走进结构体存储--位域

1、位域简介

       在嵌入式编程中,经常会遇到下面的结构:
  1. struct  _data  
  2. {  
  3.   char   a:6;  
  4.   char   b:2;  
  5.   char   c:7;  
  6. }data;  

      在存储信息的时候,我们可能并不需要占用一个完整的字节,而只需占一个或几个二进制位,如要存储一个八进制数据,只需要3 个二进制位就够了。为了节省存储空间,C 语言提供了位域这种数据结构。所谓位域,就是把存储空间中的二进制位划分为几个不同的区域,并说明每个区域的位数,每个域有一个域名,允许在程序中按域名进行操作。

2、位域的定义及变量的说明

      定义位域的一般形式如下:

  1. struct   位域结构名{  
  2.   类型说明符  位域名a:位域长度;  
  3.   ……  
  4.   类型说明符  位域名b:位域长度;  
  5. };  

     位域变量的说明:

     以开头提到结构体来说明:data 为struct _data 类型变量。占有2个字节,其中a:占6位,b占2位,c占7位;

3、位域定义的几点说明

      1)  一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也                可以有意使某位域从下一单元开始。

     2)  位域的长度不能大于指定类型固有长度,比如说int的位域长度不能超过32,bool的位域长度不能超过8。

     3) 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:

          
  1. struct data_ {  
  2.        int a:1  
  3.        int :2 /*该2位不能使用*/  
  4.        int b:3  
  5.        int c:2  
  6. }data;   

4、位域使用及存储空间说明

      1)位域的使用:

        
  1. struct   位域结构名{  
  2.   类型说明符  位域名:位域长度;  
  3.   ……  
  4.   类型说明符  位域名:位域长度;  
  5. } 位域变量名1,位域变量名2……;  
  1. /*其中,位域结构名可以省略掉,直接定义位域变量,如:*/  
  2. struct   {  
  3.   类型说明符  位域名:位域长度;  
  4.   ……  
  5.   类型说明符  位域名:位域长度;  
  6. } 位域变量名1,位域变量名2……;  
  1. /*还可以先定义位域类型,再定义位域变量名,如:*/  
  2. struct   位域结构名{  
  3.   类型说明符  位域名:位域长度;  
  4.   ……  
  5.   类型说明符  位域名:位域长度;  
  6. };  
  7. struct   位域结构名  位域变量名1,位域变量名2……;  

      2) 位域存储空间说明

    示例1:
  1. #include <stdio.h>  
  2.     struct data_ {  
  3.         char a  : 5;  
  4.         char b  : 3;  
  5.         char c  : 7;  
  6.     }data;   
  7.   
  8. int main()  
  9. {  
  10.   
  11.     printf("data 的起始地址是:%p\n",&data);  
  12.     printf("data 的占有的字节数:%d\n",sizeof(struct data_));  
  13. }  
  14. james@jsh:~$ ./a.out   
  15. data 的起始地址是:0x804a01c  
  16. data 的占有的字节数:2  

示例2:
  1. #include <stdio.h>  
  2.     struct data_ {  
  3.         char a  : 5;  
  4.         char b  : 6;  
  5.         char c  : 7;  
  6.     }data;   
  7.   
  8. int main()  
  9. {  
  10.   
  11.     printf("data 的起始地址是:%p\n",&data);  
  12.     printf("data 的占有的字节数:%d\n",sizeof(struct data_));  
  13. }  
  14. james@jsh:~$ ./a.out   
  15. data 的起始地址是:0x804a01c  
  16. data 的占有的字节数:3  

    通过上面的2个例子,我们可以充分说了,上节中的第一条:

一个位域必须存储在同一个字节中,不能跨两个字节。

示例 3:
  1. #include <stdio.h>  
  2.     struct data_ {  
  3.         char a  : 6;  
  4.         int b  : 22;  
  5.         char c  : 7;  
  6.     }data;   
  7.   
  8. int main()  
  9. {  
  10.   
  11.     printf("data 的起始地址是:%p\n",&data);  
  12.     printf("data 的占有的字节数:%d\n",sizeof(struct data_));  
  13. }  
  14. james@jsh:~$ ./a.out <span style="color:#ff0000;">  
  15. </span>data 的起始地址是:0x804a01c  
  16. data 的占有的字节数:8<span style="color:#ff0000;">  
  17. </span>  
示例4:
  1. #include <stdio.h>  
  2.     struct data_ {  
  3.         char a  : 6;  
  4.         int b  : 30;  
  5.         char c  : 7;  
  6.     }data;   
  7.   
  8. int main()  
  9. {  
  10.   
  11.     printf("data 的起始地址是:%p\n",&data);  
  12.     printf("data 的占有的字节数:%d\n",sizeof(struct data_));  
  13. }  
  14. james@jsh:~$ ./a.out   
  15. data 的起始地址是:0x804a01c  
  16. data 的占有的字节数:12  

示例5:
  1. #include <stdio.h>  
  2.     struct data_ {  
  3.         char a  : 6;  
  4.         int b  : 12;  
  5.         char c  : 7;  
  6.     }data;   
  7.   
  8. int main()  
  9. {  
  10.   
  11.     printf("data 的起始地址是:%p\n",&data);  
  12.     printf("data 的占有的字节数:%d\n",sizeof(struct data_));  
  13. }  
  14. james@jsh:~$ ./a.out   
  15. data 的起始地址是:0x804a01c  
  16. data 的占有的字节数:4  

上述测试:必须对内存对齐方式有一定的了解,简单说明下吧!内存对齐应遵循下来3个规则: 

1、按字节对齐 2、按字对齐 3、按半字对齐 (一个字是4个字节,半字2个字节)

 位域的存储首先应该遵循内存的对齐放弃,再遵循上述3节第一条部分。

再补充点就是:结构体成员排列最好的方式,按类型占有字节的大小,按大到小排列!

 
  1. #include <stdio.h>     
  2. #pragma pack (2)   
  3. struct  _data  
  4. {  
  5.   int      a:16;  
  6.   unsigned  char   b:5;  
  7.   char      c:5;  
  8. }data;  
  9. void main()    
  10. {   
  11.   int *p=(int *)&data;  
  12.   printf(" 位域结构的起始地址为:%d\n\n",p);  
  13.   data.a=2;  
  14.   printf(" 整型指针p 所指向的单元存储的值为:%d\n",*p);  
  15.   printf(" 位域a 的值为:%d\n",data.a);  
  16.    
  17.   char *p1=(char*)(p+1);  
  18.   data.b=18;  
  19.   printf("\n字符指针p1所指向的单元存储的值为:%d\n",*p1);  
  20.   printf(" 位域b 的值为:%d\n",data.b);  
  21.   data.c=255;  
  22.   char *p2;  
  23.   p2 = p1+1;  
  24.   printf("\n字符指针p2所指向的单元存储的值为:%d\n",*p2);  
  25.   printf(" 位域c 的值为:%d\n",data.c);  
  26.   return ;  
  27. }    
  28. 运行结果:  
  29. 位域结构的起始地址为:4233624  
  30. 整型指针p 所指向的单元存储的值为:2  
  31. 位域a 的值为:2  
  32. 字符指针p1所指向的单元存储的值为:18  
  33. 位域b 的值为:18  
  34. 字符指针p2所指向的单元存储的值为:31  
  35. 位域c 的值为:-1。  


 

& d a t a 为d a t a 位域结构的起始地址,将其强制转换为i n t 型指针,并赋值给p ,所以p 的
值就是d a t a 位域的起始地址,即4 2 3 3 6 2 4 ,p 指针指向的就是以4 2 3 3 6 2 4 为起始地址的连续
4 个字节的内存单元;接下来执行“c h a r   * p 1 = ( c h a r * ) ( p + 1 ) ; ”使p 1 的值为4 2 3 3 6 2 8 ,p 1 就指
向地址为4 2 3 3 6 2 8 的内存单元;执行“p 2   =   p 1 + 1 ; ”使p 2 的值为4 2 3 3 6 2 9 ,c h a r 型指针指向
地址为4 2 3 3 6 2 9 的内存单元。我们发现,* p 的值和位域a 的值相同。由此可以看出,V C + +  
6 . 0 在编译的时候,对于那些没有使用的位域段,编译器对其进行填充0 的处理。看看位域c
的运行结果,我们发现输出与输入不相符,这是因为在编译的过程中对c h a r 型位域默认执行
有符号处理,所以输出值为- 1 ,而对位域b 指定了无符号的处理方式,所以输出与输入完
全一致。

【本文转至】http://blog.csdn.net/sjin_1314/article/details/8683008
       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值