2014-01-16 184 views
0
void main() 
{ 
    struct bitfield 
    { 
     unsigned a:5; 
     unsigned c:5; 
     unsigned b:6; 
    }bit; 
    char *p; 
    struct bitfield *ptr,bit1={1,3,3}; 
    p=&bit1; 
    p++; 
    printf("%d",*p); 
} 



Explanation: 
  • 的= 1二进制值是00001(在5位)B的
  • 二进制值= 3是00011(在5位)
  • 二进制c = 3的值是000011(在6位)意外行为

    我的问题是:在内存中它将如何表示为?

当我编译它给输出12我无法弄清楚,为什么它的发生:在我看来,让我们说内存中表示将在以下格式:

00001 000011 00011 
     |  | 
     501  500 (Let Say starting address) 

请纠正我,如果我我错了。

+2

'void main()'! –

+0

什么是'ptr'用于? – harper

+0

为什么不打印* p而不增加它?你也可以打印两个字节并删除所有疑问。 –

回答

1

实际表示是这样的:

000011 00011 00001 
    b  c  a 

当作为字节对齐:

00001100 01100001 
     |  | 
     p+1  p 

在地址(P + 1)是0001100其给出12.

1

C标准并未完全指定如何将位字段打包为字节。细节取决于每个C实现。

选自C 2011 6.7.2.1:

11的实现可以分配任何可寻址的存储单元大到足以容纳一个位字段。如果剩余足够的空间,紧接在结构中的另一位字段之后的位字段应被打包到相同单元的相邻位中。如果剩余空间不足,则将不适合的位字段放入下一个单元或与相邻单元重叠是实现定义的。位单元内的位字段的分配顺序(从高位到低位或从低位到高位)是实施定义的。未指定可寻址存储单元的对齐方式。

1

从C11标准(6.7.2.1):

比特连接的视场的分配单位(高位到低位或低阶到高阶)内的顺序是实现定义的。未指定可寻址存储单元的对齐方式。

我知道一个事实,即在类Unix系统GCC和其它编译器命令,可以从操作系统源的IP报头的定义来证明在主机字节顺序位字段我不得不派上用场:

struct ip { 
#if _BYTE_ORDER == _LITTLE_ENDIAN 
     u_int  ip_hl:4,    /* header length */ 
        ip_v:4;    /* version */ 
#endif 
#if _BYTE_ORDER == _BIG_ENDIAN 
     u_int  ip_v:4,    /* version */ 
        ip_hl:4;    /* header length */ 
#endif 

其他编译器可能也这样做。由于你很可能在一台小型机器上,所以你的位域将从你期望的位置向后(除了已经倒退的位置)。很可能它在内存中看起来像这样(请注意,在你的问题中,你的字段在结构中的顺序是“a,c,b”,而不是“a,b,c”,只是为了使这更容易混淆):

01100001 00001100 
|  | 
byte 0 byte 1 
| |  |  | 
x a  b  c 

所以,所有三位字段都可以填充到一个int中。填充是自动添加的,它位于所有位字段的开始位置,它放在字节2和3处。然后b从字节1的最低位开始。在c之后它开始于字节1 2,但我们只能放入两个它的位,c的两个最高位是0,然后c继续在字节0(x在我上面的图片),然后在那之后你有a

请注意,图片的字节和左边的位都是最低的地址向右增长(这在文献中非常标准,您的图片在一个方向上有位,在另一个方向上有字节使得一切都变得更加令人困惑,特别是增加了字段“a,c,b”的奇怪排序。

我没有上述的任何感运行这个程序,然后在字节顺序读了起来:

#include <stdio.h> 
int 
main(int argc, char **argv) 
{ 
     unsigned int i = 0x01020304; 
     unsigned char *p; 
     p = (unsigned char *)&i; 
     printf("0x%x 0x%x 0x%x 0x%x\n", (unsigned int)p[0], (unsigned int)p[1], (unsigned int)p[2], (unsigned int)p[3]); 
     return 0; 
} 

然后,当你明白什么小端做的字节顺序在一个int,地图你的比特场,但场后向。然后它可能会开始有意义(我已经这么做了很多年,但仍然令人困惑)。

另一个例子来说明位字段如何向后两次,一次是因为编译器决定把他们向后一小端机器上,然后再次因为整数的字节顺序:

#include <stdio.h> 
int 
main(int argc, char **argv) 
{ 
     struct bf { 
       unsigned a:4,b:4,c:4,d:4,e:4,f:4,g:4,h:4; 
     } bf = { 1, 2, 3, 4, 5, 6, 7, 8 }; 
     unsigned int *i; 
     unsigned char *p; 
     p = (unsigned char *)&bf; 
     i = (unsigned int *)&bf; 
     printf("0x%x 0x%x 0x%x 0x%x\n", (unsigned int)p[0], (unsigned int)p[1], (unsigned int)p[2], (unsigned int)p[3]); 
     printf("0x%x\n", *i); 
     return 0; 
}