有关位域的理解和说明

关于字节序:是字节之间的事情,多字节数据类型,字节的排序问题,类似于在十进制数中,十位放在个位的左边还是右边的问题。例如int类型有4个字节,值为0x12345678,地址为0x000010~0x000013,那么在0x000010是存放0x12还是存放0x78.分下面两种情况:

    1)低地址放低位(小端字节序,小端即低位在前)。实际存储为0x78 56 34 12.  使用这种字节序的典型为x86机器,即intel和amd的cpu。
    2)低地址放高位(大端字节序)。实际存储为0x12 34 56 78. 其余的厂家一般使用这种字节序。sun,ibm等等。另外网络字节序也是这种,二进制文件如音视频文件中一般也为大端。
    在字节内部,一个字节的二进制排序,不存在大小端问题。就和平常书写的一样,先写高位,即低地址存储高位。如char a=0x12.存储从低位到高位就为0001 0010。
   如:
      struct XX
   {
   unsigned int i:3;
   unsigned int j:4;
   };
   
   XX.i = 1;
   XX.j = 3;
  
   (gdb) p xx.i
   $1 = 1
   (gdb) p xx.j
   $2 = 3
   (gdb) x/tw &xx
   0xbfaa3024: 01000000 00000001 01011100 1 0011 001
  例子中,i占据最低三bit,j占据次低四bit。 
 
    字节内部二进制排序的特殊情况,字节被位域分割的话,不同位域会根据cpu的不同采用不同排序(大端或小端)。或者说一个字节内两个或多个位域的排练顺序:从左至右还是从右至左。
    例如,在流媒体协议rtp的头部格式为:版本(2bit),填充(1bit),扩展(1bit),csrc计数(4bit),标志(1bit),负载类型(7bit),序列号(16bit),时间戳(32bit)。在intel的机器中,定义头部类型时,应该如下:(intel为小端字节序,而网络流为大端字节序,相反
     
  1. typedef struct 
  2. {
  3.     /**//* byte 0 此处,注意顺序与规定相反,在存储的时候,这个字节内部由低位到高位为version,padding,extension,csrc_len*/
  4.     uint8_t csrc_len:4; 
  5.     uint8_t extension:1; 
  6.     uint8_t padding:1; 
  7.     uint8_t version:2; 
  8.     /**//* byte 1 */
  9.     uint8_t payload:7; 
  10.     uint8_t marker:1; 
  11.     /**//* bytes 2, 3 */
  12.     uint16_t seq_no; 
  13.     /**//* bytes 4-7 */
  14.     uint32_t timestamp; 
  15.     /**//* bytes 8-11 */
  16.     uint32_t ssrc; /**//* stream number is used here. */
  17. } RTP_FIXED_HEADER;
    另外,上图,位域的顺序的说明,只在一个字节内有效。如果跨越字节,则按照声明的顺序分配。
    例2,程序如下:
  1. #include <stdio.h>
  2. int main() 
  3. { 
  4. struct bitfield { 
  5. int ia:2; 
  6. int ib:6; 
  7. } field; 
  8. field.ia=1; 
  9. field.ib=4; 
  10. char * c; 
  11. c=(char *)&field; 
  12. printf("%d\n",*c); 
  13. getchar();
  14. return 1; 
  15. }


在X86上运行结果是17(000100 01),而不是68(01 000100)

 

 

 

而当使用union进行类型间转换时,情况又有所不同,请看下面的程序:

 1 #include <stdio.h>
 2 int  main()
 3 {
 4     struct foo4 {
 5         char    a : 2;
 6         char    b : 3;
 7         char    c : 3;
 8         char    d : 2;
 9         char    e : 6;
10     };
11     union foo5
12     {
13         short s;
14         foo4 ut;
15     }m;
16     struct foo4 t;
17     t.a = 1;
18     t.b = 2;
19     t.c = 3;
20     t.d = 2;
21     t.e = 0;
22 
23     m.ut = t;
24 
25     //short s = (short)t;
26     printf("%d\n" , m.s);
27 
28     29     int len = sizeof(foo4);
30     printf("%d",len);
31     return 0;
32 }

此程序输出m.s = 617(1001101001),说明进制转换之后,依然是先声明的变量在低位,后生命的变量在高位。

但此时如果将程序中的第21行注释掉,我的环境则输出-12951,这里的初始化疏忽,也迷惑了我很久才找到,初始化的部分是对的,但是没初始化的位域部分出现随机值。请大家在写程序的时候一定要注意。

 

 

其他的注意事项:
  1.位域的长度不能大于int对象所占用的位数。
  2.由于位域的实现会因编译程序的不同而不同,因此使用位域会影响程序的可移植性,在
  不是非要使用时最好不要使用.
  3.尽管位域可以节省空间,却增加了处理时间。
  4.位域的位置不能访问,因此不能对位域使用地址运算符&,也不能使用位域的数组。
  5.带位域的结构内存中各个位域的存储方式取决于具体的编译程序:可以从左向右,也可
  一从右向左存储。

      6.通常理解的位域变量的宽度不能超过字节,但是也有特殊的情况:如果设置空白位(无变量名,仅作为占位用)则是可以跨越字节的。

posted @ 2013-11-19 17:01  freedesert  阅读(556)  评论(0编辑  收藏  举报