防止GCC在结构体联合中重新排列位域

3

有如下代码:

typedef union
{
    struct bits
    {
        uint32_t bit0 : 1;
        uint32_t bit1 : 1;
        uint32_t bit2 : 1;
        ...
        ...
        uint32_t bit14 : 1;
    }

    uint32_t value;

} MyUnion;

重要的是防止编译器重新排序位域,否则value的值将根据平台和可能甚至是已编译二进制文件的大小而变化。

是否有办法告诉编译器“不要重新排序这些字段”?

将类型为MyUnion的变量声明为volatile是否可以实现此目的?

如果unionpackedaligned(4)如果您100%确定将始终使用相同字节序的32位平台是否有可能确保位域不会重新排序?

编辑

以上内容是否只能针对一个特定的编译器进行?也就是说,是否不需要在不同的编译器之间具有兼容性。


即使没有重新排序,也无法防止“value”发生更改。 - Dan M.
简短的回答是:没有。 - Sam Varshavchik
1
是的,这是不可能的。但如果你始终使用相同字节序的32位平台,则位顺序不应更改。我找到了一篇关于位域的好文章:http://jkz.wtf/bit-field-packing-in-gcc-and-clang - Cactus'as
5
如果你需要强制规定比特的确切顺序和排列方式,那么位域(bitfields)不是合适的工具。你可以花时间尝试各种编译器扩展来摆弄,但最终你仍会感到不确定而且没有保证,我认为这会比它值得的麻烦得多。相反,如果你需要强制规定比特的确切顺序和排列方式,那就硬着头皮使用 "unsigned int" 并结合一些显式的掩码(masking)和移位(shifting)技巧。位域看起来很有诱惑力,但是,就像黄铁矿一样。 - Steve Summit
1
@Dan,包含union对所包含的struct中位域的顺序没有任何影响。位域在其底层存储单元(在您的情况下为uint32_t)中分配的方向是由实现定义的。 - Ian Abbott
显示剩余5条评论
1个回答

4
我想评论中已经给出了总体的否定答案。在这个答案中,我想回答你关于volatile的问题:
声明MyUnionvolatile并不能实现你想要的行为。另外,你不能将union声明为volatile。它是一个变量声明的修饰符。它告诉编译器变量的值可以随时改变,因此应该始终重新加载值(从内存中重新读取),而不是基于编译器之前看到的语句假设值。
这用于当变量值可能会因外部因素而改变时,例如声明与真实物理值(如微控制器中的寄存器)相关联的变量时。

“无法将联合声明为volatile” - 这是什么意思?具有联合类型的变量可以是volatile。typedef可以是union和volatile。我认为您会惊讶地发现,您可以在typedef中包含volatile和例如const,例如typedef union { int a; } volatile MyUnion; - KamilCuk
是的,你说得对,在这种情况下,类型声明(在本例中为union)当然不能是易失性的。但是我指的是类型为MyUnion的变量(我写错了),而不是union本身。现在已经修正了。 - Dan
联合体可以声明为volatile。typedef union foo { int x; float y; } Foo; typedef volatile Foo VolatileFoo; 是有效的C代码,它定义了VolatileFoo作为一个volatile联合类型。此外,在这些语句之后,Foo Convert(VolatileFoo *x) { return x; }会产生编译器消息,显示类型不兼容,表明编译器知道x指向的是一个volatile联合,而不仅仅是一个联合,从而证明volatile是类型VolatileFoo的一部分。 - Eric Postpischil

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