位字段的意外行为

前端之家收集整理的这篇文章主要介绍了位字段的意外行为前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我编译了代码,
#include <stdio.h>

struct s {
    int a : 6;
    _Bool b : 1;
    _Bool c : 1;
    _Bool d : 1;
    _Bool e : 1;
    _Bool f : 1;
    _Bool g : 1;
    int h : 12;
};

void main(void) {
    printf("%d\n",sizeof(struct s));
}

输出有点出乎意料.

12

如C11草案所述,

… If enough space remains,a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit…

因为我使用了32位编译器,所以我预计它会适合4个字节.具体来说,我使用了gcc(tdm-1)5.1.0.这是违反标准的吗?

编辑:

将所有_Bools替换为int按预期工作…我不知道为什么……

编辑:

在gcc 5.4.0中,代码按预期工作.这个问题的关键点是为什么尾随的_Bools和int不适合第一个.我想我没有做太多关于实现的假设(除了int至少4个字节,这是可以接受的),我在这里谈论C标准的C保证行为.因此,我不能同意下面的一些评论.

解决方法

这些是位字段. “预期”输出的方式并不多,因为这些标准很难指定.此外,编译器往往对它们的支持很差.

首先,您引用的完整部分(6.7.2.1/11)说:

An implementation may allocate any addressable storage unit large
enough to hold a bitfield. If enough space remains,a bit-field that
immediately follows another bit-field in a structure shall be packed
into adjacent bits of the same unit. If insufficient space remains,
whether a bit-field that does not fit is put into the next unit or
overlaps adjacent units is implementation-defined. The order of
allocation of bit-fields within a unit (high-order to low-order or
low-order to high-order) is implementation-defined. The alignment of
the addressable storage unit is unspecified.

所有这一切都意味着你几乎不能对这些位如何在内存中结束做出任何假设.你不可能知道编译器将如何排列对齐,你不可能知道位的位顺序,你无法知道签名,你无法知道endianess.

至于if int和_Bool将合并到同一个“存储单元”……好吧,为什么会这样?它们是不兼容的类型. C标准没有提到当你有两个不相容类型的相邻位字段时会发生什么 – 它可以接受主观解释.我怀疑会有各种类型的别名问题.

因此,编译器完全可以将所有这些放在单独的“存储单元”中.如果您相邻地放置相同类型的项目,我会希望它合并它们或引用的部分没有任何意义.例如,我希望从以下大小8:

struct s {
    int a : 6;
    int h : 12;
    _Bool b : 1;
    _Bool c : 1;
    _Bool d : 1;
    _Bool e : 1;
    _Bool f : 1;
    _Bool g : 1;
};

现在,如果你想要确定性的行为,你应该做什么,可移植代码是将位字段抛出窗口并使用逐位运算符.它们具有100%的确定性和便携性.

假设你实际上不想要一些神秘的签名号码字段,原始代码提示,那么:

#define a UINT32_C(0xFC000000)
#define b (1u << 18)
#define c (1u << 17)
#define d (1u << 16)
#define e (1u << 15)
#define f (1u << 14)
#define g (1u << 13)
#define h UINT32_C(0x00000FFF)

typedef uint32_t special_thing;

然后设计setter / getter函数或宏来设置/获取这个32位块的数据.写得正确,你甚至可以使这样的代码与endianess无关.

猜你在找的C&C++相关文章