GCC实现位域的一个错误

时间:2012-07-01 15:15:39

标签: gcc c99 bit-fields c11 compiler-bug

在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“结构和联合说明符”中声明:“

  

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

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

GCC行为正确,当两个成员使用相同的类型时,或者当一个是unsigned而另一个signed,但类型unsigned和{{1 GCC似乎认为它太明显了,因为它无法正确处理它们。

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

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

我正在使用带有__attribute__的gcc 4.7.0(尽管其他设置显示相同的行为。)

2 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:0)

在Mac OS X 10.7.4上使用GCC 4.7.1(自制)和GCC 4.2.1(LLVM / clang†)以及64位编译,此代码在{{4中生成-std=c99 1}}模式:

#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)