简体   繁体   English

位字段的意外行为

[英]Unexpected behavior of bit-field

I compiled the code, 我编译了代码,

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

and the output was a bit unexpected. 并且输出有点出乎意料。

12

As stated by the C11 draft, 如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... ...如果剩余足够的空间,紧跟在结构中另一个位域之后的位域应被打包到同一单元的相邻位中......

I expected it to fit into 4 bytes since I used a 32-bit compiler. 因为我使用的是32位编译器,所以我预计它会适合4个字节。 Specifically, I used gcc (tdm-1) 5.1.0. 具体来说,我使用了gcc(tdm-1)5.1.0。 Is it against the standard? 这是违反标准的吗?


EDIT: 编辑:

Replacing all the _Bool s to an int works as expected... I'm not sure why... 将所有_Bool s替换为int按预期工作......我不确定为什么......


EDIT: 编辑:

In gcc 5.4.0, the code works as expected. 在gcc 5.4.0中,代码按预期工作。 The crucial point of this question is why the trailing _Bool s and the int do not fit into the first one . 这个问题的关键点是为什么尾随的_Boolint不适合第一个 I think I didn't do much assumptions about the implementation (except int is at least 4 bytes, which is acceptable), and I'm talking here about the guaranteed behavior of C by the C standard . 我想我没有做太多关于实现的假设(除了int至少是4个字节,这是可以接受的), 我在这里谈论C标准的C保证行为 Therefore, I cannot agree with some comments below. 因此,我不能同意下面的一些评论。

These are bit-fields. 这些是位字段。 There is not much in the way of an "expected" output, as these are very poorly specified by the standard. “预期”输出的方式并不多,因为这些标准很难指定。 In addition, compilers tend to have poor support for them. 此外,编译器往往对它们的支持很差。

To begin with, the complete section that you quote (6.7.2.1/11) says: 首先,您引用的完整部分(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. 未指定可寻址存储单元的对齐。

All of this means that you pretty much can make no assumptions about how the bits end up in memory. 所有这一切都意味着你几乎不能假设这些位如何在内存中结束。 You can't know how the compiler will arrange alignment, you can't know the bit order of the bits, you can't know the signedness, you can't know the endianess. 你不可能知道编译器将如何排列对齐,你不可能知道位的位顺序,你无法知道签名,你无法知道endianess。

As for if int and _Bool will merge into the same "storage unit"... well, why would they? 至于if int_Bool会合并到同一个“存储单元”......好吧,为什么呢? They are incompatible types. 它们是不兼容的类型。 The C standard does not mention what will happen when you have two adjacent bit fields of incompatible types - it is left open to subjective interpretation. C标准没有提到当你有两个不相容类型的相邻位字段时会发生什么 - 它对主观解释持开放态度。 I suspect that there will be various type aliasing concerns. 我怀疑会有各种类型的别名问题。

So it is perfectly fine for the compiler to put all of these in separate "storage units". 因此,编译器将所有这些放在单独的“存储单元”中是完全没问题的。 If you place items of the same type adjacently, I would however expect it to merge them or the quoted part wouldn't make any sense. 如果您相邻地放置相同类型的项目,我会希望它合并它们或引用的部分没有任何意义。 For example I would expect size 8 from the following: 例如,我希望从以下大小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;
};

Now what you should do if you want deterministically behaving, portable code is to throw bit-fields out the window and use bit-wise operators instead. 现在,如果你想要确定性的行为,你应该做什么,可移植代码是将位字段抛出窗口并使用逐位运算符。 They are 100% deterministic and portable. 它们具有100%的确定性和便携性。

Assuming you actually don't want some mysterious signed number fields, which your original code hints about, then: 假设你实际上不想要一些神秘的签名号码字段,原始代码提示,那么:

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

Then design setter/getter functions or macros that sets/gets the data from this 32 bit chunk. 然后设计setter / getter函数或宏来设置/获取这个32位块的数据。 Written properly, you can even make such code endianess-independent. 写得不错,你甚至可以使这种代码与endianess无关。

My guess is that the compiler is treating the int bit-fields and _Bool bit-fields as different types. 我的猜测是编译器将int位字段和_Bool位字段视为不同的类型。 So it combines all of the _Bool bit-fields together and the int bit-fields together, but does not combine _Bool and int bit-fields. 因此它将所有_Bool位域和int位域组合在一起,但不组合_Boolint位域。 So the structure would be put together something like this to make 12 bytes: 所以结构将这样组合成12个字节:

struct s {
    int a : 6; // no more int bit-fields, so 4 bytes (usual size)

    // 6 bits fits in one byte, but these might have to be aligned 
    // on a 4 byte boundary for efficient access, so 4 bytes in total
    _Bool b : 1; // combine
    _Bool c : 1; // combine
    _Bool d : 1; // combine
    _Bool e : 1; // combine
    _Bool f : 1; // combine
    _Bool g : 1; // combine with above _Bool bit-fields to make 6 bits

    int h : 12; // no more int bit-fields around again, so 4 bytes
};

EDIT: This paragraph from the 2011 C standard could be interpreted to make _Bool bit-fields act different: 编辑:2011 C标准中的这一段可以解释为_Bool位字段的行为不同:

6.7.2.1 Structure and union specifiers 6.7.2.1结构和联合说明符
A bit-field is interpreted as having a signed or unsigned integer type consisting of the specified number of bits. 位字段被解释为具有由指定位数组成的有符号或无符号整数类型。 If the value 0 or 1 is stored into a nonzero-width bit-field of type _Bool, the value of the bit-field shall compare equal to the value stored; 如果将值0或1存储到_Bool类型的非零宽度位字段中,则位字段的值应等于存储的值; a _Bool bit-field has the semantics of a _Bool. _Bool位字段具有_Bool的语义。

If a _Bool bit-field has the semantics of a _Bool, then perhaps the implementer interpreted that as to say "Act like _Bool b : 1; is equivalent to _Bool b; " 如果一个_Bool位字段具有_Bool的语义,那么实现者可能会解释为“像_Bool b : 1;那样_Bool b : 1;相当于_Bool b;

A quick test here seems to confirm this theory. 这里的快速测试似乎证实了这一理论。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM