如何计算包含一些未命名成员和零大小位域的位域结构的大小?

How is the size of structures with bit-fields are calculated which contains some unnamed members and zero sized bit-fields?

提问人:Neeraj-Kumar-Coder 提问时间:1/2/2021 最后编辑:chqrlieNeeraj-Kumar-Coder 更新时间:1/2/2021 访问量:136

问:

这个结构体的大小是如何计算的:

struct B {
    unsigned char c1 : 1;
    unsigned char : 2;
    unsigned char c2 : 2;
    unsigned char : 0;
    unsigned char c3 : 4;
    unsigned char c4 : 1;
};

这代表什么?,这是否意味着它占用了 0 位(即没有内存)0

有人可以向我解释一下它的大小是如何计算的吗?

C 结构 联合 位域

评论

0赞 IrAM 1/2/2021
大小为零的未命名位字段可以强制分解填充
0赞 Neeraj-Kumar-Coder 1/2/2021
结构的内存@IrAM按照成员的写入顺序分配的(即从上到下)?
0赞 Andrew Henle 1/2/2021
位域的排序以及关于它们的几乎所有内容完全是实现定义的,并且是不可移植的:“实现可以分配任何足够大的可寻址存储单元来容纳位域。如果有足够的空间,则紧随结构中另一个位字段的位字段应打包到同一单元的相邻位中。如果空间不足,则是将不适合的位域放入下一个单元还是与相邻单元重叠,则由实现定义。...
0赞 Andrew Henle 1/2/2021
(续)......单元内位域的分配顺序(高阶到低阶或低阶到高阶)是实现定义的。可寻址存储单元的对齐方式未指定。因此,如果没有您正在使用的 C 编译器和操作系统的详细信息,您将无法获得完整的答案。

答:

1赞 dbush 1/2/2021 #1

大小为 0 的位字段用于指定将任何后续位字段放置在单独的字节/单位中。因此,结构体的布局可能如下所示:

| c1|       |   c2  |           |      c3       | c4|           |
-----------------------------------------------------------------
|  0|  1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13| 14| 15|
-----------------------------------------------------------------

如果没有大小为 0 的字段,它可能如下所示:

| c1|       |   c2  |      c3       | c4|                       |
-----------------------------------------------------------------
|  0|  1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13| 14| 15|
-----------------------------------------------------------------

但请注意,结构中位字段的顺序是实现定义的,因此它可能看起来不完全像这样。

评论

0赞 Eric Postpischil 1/2/2021
该问题不要求结构的布局,除了 .它询问大小是如何计算的。: 0
1赞 chqrlie 1/2/2021 #2

位字段长度表示启动新的存储单元0

问题中结构的一种可能布局是:

  • 成员的初始存储单元,长度为 2 的未命名成员和成员 ,c1c2
  • 成员和 .c3c4

这两个存储单元都可以小到一个字节,因为每个存储单元只包含 5 位信息,从而形成一个 2 字节的对象,即:。但其他大小也是可能的,因为存储单元的选择是定义的,并且在成员之间和结构的末端也可以进行额外的填充。任何大于可能的尺寸。可以说,即使在一个字节可以存储至少 10 位的架构上,也不可能有 的大小,因为成员规范需要 2 个存储单元。struct Bsizeof(struct B) == 221unsigned char : 0;

以下是 C 标准的定义:

6.7.2.1 结构和联合说明符

禁忌
[...]
4 指定位域宽度的表达式应为整数常量表达式,其非负值不超过省略冒号和表达式时所指定类型的对象的宽度。如果该值为零,则声明不应有声明符。

5 位域的类型应是 、 、 或其他实现定义的类型的限定或非限定版本。是否允许原子类型由实现定义。_Boolsigned intunsigned int

语义
[...]
9 结构或联合的成员可以具有除可变修改类型以外的任何完整对象类型。此外,成员可以声明由指定数量的位(包括符号位,如果有的话)组成。这样的成员称为位域;它的宽度前面有一个冒号。

10 位域被解释为具有由指定位数组成的有符号或无符号整数类型。如果值 0 或 1 存储在非零宽度的位字段中,则该位字段的值应与存储的值进行比较;位域具有 ._Bool_Bool_Bool

11 实现可以分配任何足够大的可寻址存储单元来容纳位域。如果有足够的空间,则紧随结构中另一个位字段的位字段应打包到同一单元的相邻位中。如果空间不足,则是将不适合的位域放入下一个单元还是与相邻单元重叠,则由实现定义。单元内位域的分配顺序(高阶到低阶或低阶到高阶)是实现定义的。可寻址存储单元的对齐方式未指定。

12 没有声明符,只有冒号和宽度的位域声明表示未命名的位域。作为特殊情况,宽度为 的位域结构成员表示不再将位域打包到放置前一个位域(如果有)的单元中。0

因此,实现定义了如何将 和 放置在它们的公共存储单元中,和 也是如此。图10意味着分配单元的选择是被定义的,因此的大小也是被定义的:它可以是、、等。事实上,存储单元不需要具有相同的尺寸,并且允许填充,因此任何尺寸都大于理论上的可能性。c1c2c3c4struct B2481

缺乏更精确和可移植的位域规范会适得其反,因为这些位域可以很好地映射物理硬件设备。早期 C 编译器中实现的差异以及与字节序约束相关的实际约束促使了规范和实现变化的这种灵活性,尤其是在有符号位域方面,使程序员不愿意使用此功能。

评论

0赞 Eric Postpischil 1/2/2021
虽然位域需要两个存储单元,但假设结构的大小可以是任何大于 1 的整数,因为 C 标准允许在结构的末尾进行填充,而不受约束。此外,我不清楚用于位域的“存储单元”是否在每个实例中都必须相同。如果我有,实现可以把 和 放在一个字节和一个四字节的单位中吗?struct foo { int a : 3, b : 5, c : 30 };abc
0赞 chqrlie 1/2/2021
@EricPostpischil:我同意你的看法。对齐也是实现定义的,因此打包和在一个字节中并存储在相邻的 32 位字中是可能的,尽管不太可能。是否跨两个 32 位单元也是实现定义的。abcc