【问题标题】:Initializing bit-fields初始化位域
【发布时间】:2014-06-29 19:37:30
【问题描述】:

当你写作时

struct {
    unsigned a:3, b:2;
} x = {10, 11};

x.b 是否被 ANSI C (C89) 保证为 3?我已经阅读并重新阅读了标准,但似乎无法找到确切的情况。

例如,“不能用 得到的无符号整数类型以数字为模减少 比可以表示的最大值大一 产生的无符号整数类型。”说的是计算,而不是初始化。而且,位域并不是真正的类型。

另外,(当谈到无符号 t:4 时)“包含 [0,15] 范围内的值”,但这并不一定意味着初始化器必须以 16 为模减少映射到 [0,15]。

结构初始化确实是煞费苦心地详细描述,但我似乎真的找不到确切的行为。 (当然编译器就是这样做的。IBM 文档说“当您将超出范围的值分配给位字段时,会保留低位模式并分配适当的位。”,但我想要了解 ANSI C 是否对其进行了标准化。

【问题讨论】:

  • 如果将位域视为实体,并按字面解释标准,那么“任何初始化器都不应尝试为未包含在正在初始化的实体中的对象提供值”将禁止此类初始化为尝试为位域之外的位提供值。我不确定这种解释是否正确。在任何情况下,我都会避免这种情况,而是使用明确的 {10 & 0x07, 11 & 0x03} 初始化程序。哦,我同意你的观点,标准在这个问题上不够具体。
  • 当然没有人会用真实的代码来写这个。这是 C89 课程测试中的一个潜在问题。我认为这不是一个好问题,因为有人可能会做出不同的回答,而我们仍然无法证明他是错的。
  • 我同意,这对于测试来说将是一个糟糕的问题,除非您正在测试一个打算编写编译器的人(即使那样这将是一个非常棘手的问题)。
  • 只是为了确保没有误解......不,测试的问题不是“x.b 保证为 3”,而只是“x.b 会是什么”。他们不必知道标准的每一个字母,如果有挑剔的人说没有指定,我们只是想掩饰自己。 :-)

标签: c struct initialization ansi bit-fields


【解决方案1】:

"ANSI C"/C89 has been obsolete for 25 years。因此,我的回答引用了当前的 C 标准 ISO 9899:2011,也称为 C11。


几乎所有与 C 标准中的位域相关的内容都定义不明确。通常,您不会发现任何明确解决位字段行为的内容,但它们的行为是相当隐含地指定的,“行之间”。这就是为什么你应该避免使用位域。

但是,我相信这种特定情况是明确定义的:它应该像任何其他整数初始化一样工作。

您提到的详细结构初始化规则(6.7.9)显示了初始化列表中的文字11与变量b的关系。这没什么奇怪的。然后适用的是“简单分配”,就像你写x.b = 11;一样。

在 C 中进行任何类型的赋值或初始化时,右操作数会转换为左操作数的类型。这是由 C11 6.5.16 指定的:

在简单赋值(=)中,右操作数的值被转换 赋值表达式的类型并替换存储的值 在左操作数指定的对象中。

在您的情况下,int 类型的文字 11 被转换为 unsigned int:2 的位字段。

因此,您要查找的规则应在处理转换的章节 (C11 6.3) 中找到。适用的是您在问题 C11 6.3.1.3 中已经引用的内容:

...如果新类型是无符号的,则重复转换该值 比最大值多加或减一 以新类型表示,直到值在新类型的范围内 输入。

unsigned int:2 的最大值为 3。比最大值大一为 3+1=4。编译器应该反复从值 11 中减去它:

11 - (3+1) = 7    does not fit, subtract once more:
 7 - (3+1) = 3    does fit, store value 3

当然,这与取十进制值 11 的 2 个最低有效位并将它们存储在位域中是完全相同的。

【讨论】:

  • 很好的答案,很好的观点。你会相信任何来源的任何编译器都能可靠地实现 C89 标准吗?如果它是一个旧的编译器,你不试试吗?如果它是一个新的编译器,谁在乎呢?
  • 查看原始问题的评论:这不是关于任何特定的编译器,而是关于名为“C89”的柏拉图实体。 :-)
【解决方案2】:

WRT“谈论计算,而不是初始化”,C89 标准明确地将赋值和转换规则应用于初始化。它还说:

位域被解释为由指定位数组成的整数类型。

考虑到这些,虽然编译器警告显然是有序的,但标准似乎保证丢弃高位。

【讨论】:

  • 啊,是的……那句话不知怎的让我回避了。谢谢。
  • 但是,从字面上看,这显然是一个谎言:sizeof(x.b) 不是 2/CHAR_BIT。 :-)
  • @Veky 我认为位字段仍然是unsigned 的细分。编译器弄乱了这些位,而不是你。如果你想降到比特级别,恐怕你必须自己记账。
猜你喜欢
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多