9

在 C11 中工作,以下结构:

struct S {
  unsigned a : 4;
  _Bool    b : 1;
};

由 GCC 布局为一个unsigned(4 个字节),其中使用了 4 个位,然后是一个_Bool(4 个字节),其中使用了 1 个位,总大小为 8 个字节。

请注意,C99 和 C11 特别允许_Bool作为位域成员。C11 标准(可能还有 C99)还在 §6.7.2.1 'Structure and union specifiers' ¶11 中声明:

实现可以分配任何大到足以容纳位字段的可寻址存储单元。如果有足够的空间剩余,紧跟在结构中另一个位域之后的位域将被打包到同一单元的相邻位中。

所以我认为b上面的 member 应该已经被打包到为 member 分配的存储单元中a,从而产生了一个总大小为 4 字节的结构。

GCC 行为正确,并且在对两个成员使用相同类型时,或者当一个 isunsigned和另一个时,确实会发生打包,但是GCC 似乎认为signed类型unsigned和类型过于不同,无法正确处理它们。_Bool

有人可以确认我对标准的解释,这确实是一个 GCC 错误吗?

我也对解决方法感兴趣(一些编译器开关,编译指示,__attribute__...)。

我正在使用 gcc 4.7.0 -std=c11(尽管其他设置显示相同的行为。)

4

2 回答 2

10

所描述的行为与 C99 和 C11 标准不兼容,但提供了与 MSVC 编译器(具有不寻常的结构打包行为)的二进制兼容性。

幸运的是,它可以在__attribute__((gcc_struct))应用于结构的代码中禁用,也可以使用命令行开关禁用-mno-ms-bitfields(请参阅文档)。

于 2012-07-01T15:56:41.493 回答
0

在 64 位编译的 Mac OS X 10.7.4 上使用 GCC 4.7.1(自制)和 GCC 4.2.1(LLVM/clang†),此代码4-std=c99模式下产生:

#include <stdio.h>

int main(void)
{
    struct S
    {
        unsigned a : 4;
        _Bool    b : 1;
    };
    printf("%zu\n", sizeof(struct S));
    return 0;
}

这是您在 Windows 上报告的大小的一半。它对我来说似乎大得惊人(我希望它的大小为 1 字节),但平台的规则就是它们。基本上,编译器没有义务遵循您想要的规则;它可能会遵循运行平台的规则,如果有机会,它甚至可以定义运行平台的规则。

以下程序的行为有点可疑(因为它在最后一次写入u.i之后访问u.s),但显示该字段a存储在 4 个最低有效位中,并且该字段b存储在下一位中:

#include <stdio.h>

int main(void)
{
    union
    {
        struct S
        {
            unsigned a : 4;
            _Bool    b : 1;
        } s;
        int i;
    } u;
    u.i = 0;
    u.s.a = 5;
    u.s.b = 1;
    printf("%zu\n", sizeof(struct S));
    printf("%zu\n", sizeof(u));
    printf("0x%08X\n", u.i);
    u.s.a = 0xC;
    u.s.b = 1;
    printf("0x%08X\n", u.i);
    return 0;
}

输出:

4
4
0x00000015
0x0000001C

† i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1(基于 Apple Inc. build 5658)(LLVM build 2336.9.00)

于 2012-07-01T17:16:26.717 回答