C/C++ 中的位字段:什么是保证的,什么是实现定义的?

Bit-Fields in C/C++: what is guaranteed, what is implementation-defined?

提问人:wimalopaan 提问时间:1/30/2023 最后编辑:Eric Postpischilwimalopaan 更新时间:1/31/2023 访问量:460

问:

https://en.cppreference.com/w/c/language/bit_field 读,以下结论正确吗?

  • 相邻的位域之间没有填充(这在 C 标准的 6.7.2.1 中似乎有所不同)。
  • 位域在存储单元中的放置是实现定义的。
  • 位域内位的位置由实现定义。

(对于 C++,另请参阅:C++ 中位字段的特征

c 位字段 实现定义的行为

评论

0赞 Support Ukraine 1/30/2023
从草案 N1570 开始:实现可以分配任何足够大的可寻址存储单元来容纳位域。如果有足够的空间,则紧跟在结构中另一个位域之后的位域应打包到同一单元的相邻位中。如果剩余空间不足,则将不适合的位域放入下一个单元或与相邻单元重叠由实现定义。单元内位域的分配顺序(高阶到低阶或低阶到高阶)是实现定义的。未指定可寻址存储单元的对齐方式
2赞 chux - Reinstate Monica 1/30/2023
wimalopaan,这个问题很宽泛。它几乎是对位域各个方面的要求。也许更窄的东西?
1赞 Support Ukraine 1/30/2023
@wimalopaan 在我看来,我上面发布的引述涵盖了您的第一点。
1赞 463035818_is_not_an_ai 1/30/2023
实际上,我认为您的所有观点都包含在“位字段的以下属性是实现定义的:[...]关于类对象中位字段的实际分配细节的所有信息”
1赞 Richard Critten 1/30/2023
@wimalopaan位字段 - 您需要阅读您的实现文档,因为关于它们的所有重要内容都是“实现定义的”。如果您想要可移植位域,请不要使用它们。相反,使用固定宽度的整数类型,并带有您自己的遮罩和打包/解包。

答:

3赞 John Bollinger 1/31/2023 #1

作为初步,没有问题标题引用的语言“C/C++”。C 和 C++ 是不同的语言,共享一个共同的子集。特别是,C 不是 C++ 的子集。

关于C语言,当前语言规范(目前为C17)提供的关于位域布局的所有细节都在第6.7.2.1/11-12段中。

以下结论正确吗?

  • 相邻的位域之间没有填充(这在 C 标准的 6.7.2.1 中似乎有所不同)。

位字段不直接在结构中布局。C 实现在结构中为它们布置了“可寻址存储单元”,并在这些单元中布置了位字段。ASU 的尺寸和对齐要求未指定。

该规范确实指出,如果 ASU 中有足够的空间分配给一个位域,则紧随其后的位域将打包到同一 ASU 的相邻位中。这意味着这些位域之间没有填充位。但是,如果没有足够的空间,则由实现定义紧随的位域是跨越两个 ASU,还是将其所有位分配给一个单独的 ASU,在第一个 ASU 中保留未使用的(填充)位。此外,零宽度位域可用于强制将它后面的位域分配给新的 ASU,可能需要在前一个 ASU 中填充位。

此外,该规范对 ASU 之间是否存在填充字节没有任何说明。ASU 不需要大小一致或彼此具有相同的对齐要求,因此,即使在在这方面没有故意反常的实现中,有时它们之间也需要填充字节是合理的。

  • 位域在存储单元中的放置是实现定义的。

该规范明确指出,ASU 中位域的顺序是实现定义的。这是从右到左从左到右的意义。“订单”与“放置”并不完全相同,但我想这就是你的意思。

  • 位域内位的位置由实现定义。

没有。这是一个表述问题,而不是布局问题,C17的相关段落是6.2.6.1/3-4:

存储在无符号位域和类型对象中的值应使用纯二进制表示法表示。unsigned char

[...]存储在位字段中的值由 m 位组成,其中 m 是 为位字段指定的大小。对象表示是集合 M位,位域包含在可寻址存储单元中 拿着它。

脚注 49 澄清了“纯二进制表示法”的含义。位域表示的所有其他细节都是未指定或未定义的,不是实现定义的,这意味着您不能依赖它们被记录下来。


C++ 中的差异包括但不限于:

  • C++ 正式批准比 C 更多的位字段声明类型。
  • C++ 定义了一种机制,用于声明包含填充位的位字段,但 C 没有。
  • 位域分配和对齐是用 C++ 定义的实现(而不是在 C 中指定)。

C++ 规范的相关部分是 [class-bit],当前草案规范中的 11.4.10。