【问题标题】:Prevent GCC from reordering bitfields in unions of structs防止 GCC 重新排序结构联合中的位域
【发布时间】:2019-08-07 11:01:00
【问题描述】:

有一段代码如下:

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 会实现此目的吗?

如果union 是packed 和aligned(4),如果您100% 确定您将始终使用32 位平台,是否可以确保位域不会被重新排序, 具有相同的字节顺序?

编辑

是否可以仅针对一种特定的编译器完成上述操作?也就是说,如果不需要跨不同编译器兼容的话。

【问题讨论】:

  • 没有什么能阻止value即使没有重新排序也能改变。
  • 简短的回答是:不,没有。
  • 是的,这是不可能的。但是,如果您总是使用具有相同字节顺序的 32 位平台,则位顺序不应该改变。我找到了一篇关于位域的好文章jkz.wtf/bit-field-packing-in-gcc-and-clang
  • 如果您需要强制位的确切顺序和排列,位域不是适合这项工作的工具。你可以花时间修修补补,你可以尝试使用各种编译器扩展,但最终你仍然不确定并且没有保证,而且(在我看来)这将比它的价值更麻烦。相反,如果您需要强制位的确切顺序和排列,只需硬着头皮使用unsigned int 以及一些显式的屏蔽和移位技术。位域很诱人,但pyrite 也是如此。
  • @Dan 包含的union 对包含的struct 中的位域顺序没有任何影响。位域在其底层存储单元中分配的方向(在您的情况下为uint32_t)是实现定义的。

标签: c++ c gcc compiler-optimization bit-fields


【解决方案1】:

我认为 cmets 已经给出了总体“否”的答案。有了这个答案,我想解决你关于volatile的问题:

将MyUnion 声明为volatile 会实现此目的吗?

volatile 关键字不会改变您在此处寻找的任何行为。此外,您不能将联合声明为volatile。它是一个关键字,是变量声明的限定符。它告诉编译器变量值可以随时更改,因此应该始终重新加载该值(如从内存中重新读取),而不是根据编译器看到的先前语句假定该值。 这用于当变量值可能因外部因素而改变时,例如在声明与真实物理值(例如微控制器中的寄存器)相关联的变量时。

【讨论】:

  • can't declare a union as volatile - 这是什么意思?具有 union 类型的变量可以是 volatile。 typedef 可以是 union 和 volatile。我想你会惊讶于你可以包含volatile 和前任。 const 在 typedef 中,例如 typedef union { int a; } volatile MyUnion;。
  • 是的,你是对的,类型声明(在本例中为 union)当然不能是 volatile 的。但我指的是MyUnion 类型的变量(我写错了),而不是union 本身。现在它已经修复了。
  • 联合可以声明为 volatile。 typedef union foo { int x; float y; } Foo; typedef volatile Foo VolatileFoo; 是有效的 C 代码,将 VolatileFoo 定义为 volatile 联合类型。此外,在这些语句之后,Foo Convert(VolatileFoo *x) { return x; } 产生一个类型不兼容的编译器消息,表明编译器知道x 指向一个可变联合,而不仅仅是一个联合,从而证明volatile 是@ 类型的一部分987654338@.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-08
  • 1970-01-01
  • 2020-12-02
  • 2019-06-19
  • 1970-01-01
  • 2014-02-08
  • 2013-03-21
相关资源
最近更新 更多