C中位字段运算符的意外行为

  • 本文关键字:意外 运算符 字段 c
  • 更新时间 :
  • 英文 :

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:
  • a=1的二进制值为00001(5位)
  • b=3的二进制值为00011(以5位为单位)
  • c=3的二进制值为000011(6位)

    我的问题是:在记忆中,它将如何表示为?

当我编译时,它给出了输出12,我不知道为什么会发生这种情况:在我看来,假设内存表示将采用以下格式:

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

如果我错了,请纠正我。

实际表示如下:

000011 00011 00001
b     c     a

当对齐为字节时:

00001100 01100001
|        |
p+1      p 

在地址(p+1)上是CCD_ 1,它给出12。

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

摘自C 2011 6.7.2.1:

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

来自C11标准(6.7.2.1):

一个单元内比特场的分配顺序(从高阶到低阶或从低阶到高阶)由实现定义。未指定可寻址存储单元的对齐方式。

我知道GCC和类unix系统上的其他编译器按主机字节顺序排列位字段,这可以从我手边的操作系统源代码的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。然后CCD_ 2从字节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%xn", (unsigned int)p[0], (unsigned int)p[1], (unsigned int)p[2], (unsigned int)p[3]);
return 0;
}

然后,当您了解little-endian对int中字节排序的作用时,将您的位字段映射到它的顶部,但将字段向后映射。然后它可能开始有意义了(我已经做了很多年了,但它仍然令人困惑)。

另一个例子显示了位字段是如何向后两次的,一次是因为编译器决定将它们向后放在小端序机器上,另一次是由于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%xn", (unsigned int)p[0], (unsigned int)p[1], (unsigned int)p[2], (unsigned int)p[3]);
printf("0x%xn", *i);
return 0;
}

最新更新