什么是零宽度位域?

18
为什么有些结构体具有零宽度的位域,以及为什么需要它?这个问题似乎与Practical Use of Zero-Length Bitfields类似。
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

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

1
这是微软的答案,我建议你看一下:http://msdn.microsoft.com/en-us/library/ewwyfdbe(v=vs.71).aspx,即便如此,对齐方式始终取决于编译器。 - code-gijoe
结构体 { int a:3; int b:2; int :0; int c:4; int d:3; }; 这将给出一个布局000aaabb 0ccccddd 而不是没有 :0;0000aaab bccccddd 0宽度字段告诉后续的位域应设置在下一个原子实体(char)上。按位字段声明的数据排序从低位到高位,或者根据您的架构而定。 - manav m-n
在我的实验中,每次在Linux gcc7.2上运行时,d的值都会不同。然而,在Windows mingw 5.3下输出将保持不变。因此,它在很大程度上取决于编译器。 - Lewis Chan
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;
}

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 ;

3
记忆布局是“因情况而异”的,你不能依赖于任何特定编译器的任何特定布局。事实上,通过更改编译器的设置,您可能会看到来自给定编译器的不同布局。不要试图猜测、直觉或者依赖于布局。
负数 - 所有元素都是int类型,它们是带符号的,因此它们是负数,因为您已将所有位初始化为1,所以您的符号位被设置了。至于d-我不知道,是笔误吗?

你没有回答问题的主要部分 - 什么是零宽度位域的目的? - Paul R

1

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


“Numbers are negative because the bitfields are signed” 的翻译不太准确。OP的“int”可以被解释为无符号字段。这是C语言中“int”和“signed int”可能不同的一个角落。参考链接 - chux - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接