18

可能的重复:
零长度位域的实际使用

为什么某些结构具有零宽度位字段,为什么需要它?

struct foo {
  int    a:3;
  int    b:2;
  int     :0; // Force alignment to next boundary.
  int    c:4;
  int    d:3;
};

int main()
{
        int i = 0xFFFF;
        struct foo *f = (struct foo *)&i;
        printf("a=%d\nb=%d\nc=%d\nd=%d\n", f->a, f->b, f->c, f->d);
        return 0;
}

上述程序的输出是

manav@os-team:~/programs/test$ ./a.out
a=-1
b=-1
c=-8
d=0

请解释为什么这些值为负数,以及这些变量在结构内部的内存布局?

4

4 回答 4

12

谷歌搜索的第一次点击开始

长度为 0 的位域必须是未命名的。无法引用或初始化未命名的位域。零宽度位字段可以导致下一个字段在下一个容器边界上对齐,其中容器的大小与位字段的基础类型相同。

至于问题的第二部分,您将结构中的一些位字段设置为全 1,并且由于这些字段已签名,因此这会导致这些字段的值为负。如果将整个结构设置为 1 并查看有符号和无符号表示中的值,您可以更有效地看到这一点,例如

int main()
{
    struct foo f;
    memset(&f, 0xff, sizeof(f));
    printf("a=%d\nb=%d\nc=%d\nd=%d\n", f.a, f.b, f.c, f.d); // print fields as signed
    printf("a=%u\nb=%u\nc=%u\nd=%u\n", f.a, f.b, f.c, f.d); // print fields as unsigned
    return 0;
}
于 2012-12-10T14:26:14.363 回答
3

内存布局是“取决于”的,您不能指望来自任何特定编译器的任何特定布局。事实上,通过更改其设置,您可能会看到与任何给定编译器不同的布局。不要试图猜测、直觉或依赖布局。

负数 - 你所有的元素都是int有符号的,所以它们是负数,因为你已经将每一位初始化为 1,所以你已经设置了符号位。至于 d - 打败了我。错字?

于 2012-12-10T14:26:40.377 回答
3

如此处所述,零长度位域增加了位域之间的对齐。如果我们连续有多个位域,它们的布局是紧凑的,但是如果我们想将其中一个对齐到字节/字/双字边界,我们需要在这个和前一个之间放置一个长度为零的位域。

来自上面链接的示例:

struct on_off {
                  unsigned light : 1;
                  unsigned toaster : 1;
                  int count;            /* 4 bytes */
                  unsigned ac : 4;     // this and
                  unsigned : 4;        // this and
                  unsigned clock : 1;  // this bitfields are next to each other
                  unsigned : 0;
                  unsigned flag : 1;   // this bitfield is at a 4 bytes boundary.
                 } kitchen ;
于 2012-12-10T14:31:12.957 回答
1

数字是负数,因为位域是有符号的,也就是说,如果你有一个signed char变量,它的大小是 8 位,可以容纳 256 个不同的值。一半的值为正,其余为负和 0。最高有效位表示符号(1 表示负,0 表示正)。关于零长度位域,请参见此处:零长度位域的实际使用

于 2012-12-10T14:27:14.357 回答