【问题标题】:Is it well-defined behavior to address a 32-bit `int` using a bitfield inside a union?使用联合中的位域来处理 32 位“int”是否是明确定义的行为?
【发布时间】:2016-03-03 01:08:00
【问题描述】:

考虑以下小程序,它使用联合来直接分配整数的位,而不是使用位运算。 print 语句打印正确,但这并不意味着它总是有效。

这是在 C 中明确定义的行为吗?

#include <stdio.h>
#include <inttypes.h>


union IntBitField{
    int32_t foo;

    struct bitfield {
        unsigned int BIT0:1;
        unsigned int BIT1:1;
        unsigned int BIT2:1;
        unsigned int BIT3:1;
        unsigned int BIT4:1;
        unsigned int BIT5:1;
        unsigned int BIT6:1;
        unsigned int BIT7:1;
        unsigned int BIT8:1;
        unsigned int BIT9:1;
        unsigned int BIT10:1;
        unsigned int BIT11:1;
        unsigned int BIT12:1;
        unsigned int BIT13:1;
        unsigned int BIT14:1;
        unsigned int BIT15:1;
        unsigned int BIT16:1;
        unsigned int BIT17:1;
        unsigned int BIT18:1;
        unsigned int BIT19:1;
        unsigned int BIT20:1;
        unsigned int BIT21:1;
        unsigned int BIT22:1;
        unsigned int BIT23:1;
        unsigned int BIT24:1;
        unsigned int BIT25:1;
        unsigned int BIT26:1;
        unsigned int BIT27:1;
        unsigned int BIT28:1;
        unsigned int BIT29:1;
        unsigned int BIT30:1;
        unsigned int BIT31:1;
    } bar;
} FooBar;
int main(){
    FooBar.foo = 0;
    printf("Size of the union: %zu\n", sizeof(union IntBitField));
    printf("Before setting any bits %"PRId32"\n", FooBar.foo);
    FooBar.bar.BIT31 = 1; // Highest order bit
    printf("Set BIT31 %"PRId32"\n", FooBar.foo);
}

我查看了this questionthis question,以及this question,但我仍然不确定。

【问题讨论】:

  • int 并不总是 32 位长。
  • @MikeCAT,我已经更正了这个问题以更好地表达我的意图。
  • 你应该包含inttypes.h并使用"%"PRId32而不是"%d"来打印int32_t
  • @MikeCAT,这在技术上是正确的,但它与问题正交。如果您想对其进行编辑,请随时进行。
  • @merlin2011:请记住,问题不仅是你、我或 Mike 阅读的,还有那些倾向于养成坏习惯的初学者,比如在格式字符串中使用错误的类型说明符。对于您的代表,您的问题不应该出现如此简单的错误。另外,我不明白您为什么不始终使用int32_t。这将使对int 宽度的担忧变得无关紧要。我也不确定为什么你只想将-10 存储在位中(假设为2 的补码int)。我认为有点应该符合_Bool 和一般的布尔运算符,因此01

标签: c gcc language-lawyer unions bit-fields


【解决方案1】:

答案是代码将始终编译/运行(这意味着它是“标准的”),但它不是“可移植的”,因为不能保证最后一行 foo 的值。不同的编译器实现可以将位字段存储在内存的不同位中,即内存 foo 引用。事实上,在考虑填充位域时,甚至可能不会完全与 foo 重叠。

这就是您链接到的第一个答案想要表达的意思。

【讨论】:

  • 如果我强制对齐,它会变得便携吗?另外,您能否链接到表明它不可移植的标准文档?
  • 不是真的 - 它也取决于系统的编译器和字节序,所以不是。如果您真的想要跨平台、可移植的位操作,您必须为您的确切目标滚动您自己的或使用位数组库。
  • 我添加了gcc 作为标签。
  • 此处的参考资料和详细信息以及底部的链接:en.cppreference.com/w/cpp/language/bit_field
  • 如果 int 是 32 位的,那么我认为它可以保证完全重叠(结构可能没有初始填充,并且有一个规则是下一个位域必须存储在同一个如果未满,单位作为前一个位域)
【解决方案2】:
  1. 可以将int 用于位字段。最好将signed int 用于位字段。最好使用unsigned。这是intsigned int 不同的地方(也许是唯一的地方)。 signed int xxx:1; 取值 0 或 -1。 int xxx:1; 采用 0 or 1 0 or -1 的值 - 它由实现定义。

  2. FooBar.bar.BIT31 = 1; // Highest order bit 不可移植,因为BIT31 可能是最低有效位。代码依赖于某个字节序。

  3. int/unsigned 宽度变化为 16、32 或 64 位的系统上,可移植性会受到影响。

  4. 使用long BIT31:1;uint32_t BIT31:1; 可能不可移植,因为任何比int/unsigned 更宽的位文件要么是实现定义的(或可能的UB)。

  5. 考虑到可能存在大量移植问题,强烈建议不要对可移植代码段使用位域。

  6. 如果总位宽不是sizeof(unsigned)*CHAR_BIT 的倍数和/或字段尝试跨越对齐限制位宽,则可以预期填充。这通常是一个严重的并发症。

  7. 我使用位域的唯一地方是映射硬件寻址的内存并且该部分代码需要限制(如果有的话)可移植性。

结论:

使用联合中的位字段来处理 32 位 int unsigned 是否是明确定义的行为?

一般不会。在特定应用程序中,通常是的,当使用不会导致填充的 unsigned 字段时。

【讨论】:

    猜你喜欢
    • 2011-04-19
    • 1970-01-01
    • 2012-07-04
    • 1970-01-01
    • 2017-09-04
    • 2013-09-10
    • 1970-01-01
    • 1970-01-01
    • 2011-09-02
    相关资源
    最近更新 更多