C-使用位域时的结构大小以及如何将其存储在存储器中

void main() { struct bitfield { signed int a :3; unsigned int b :13; unsigned int c :1; }; struct bitfield bit1 = { 2, 14, 1 }; clrscr(); printf("%d", sizeof(bit1)); getch(); } 

为什么大小为4字节? 这些元素究竟是如何存储在内存中的?

几乎每个比特字段都是实现定义的。 甚至’plain int ‘位字段的签名也是实现定义的; 它可能是签名或未签名。 字段的布局 – 无论它们是从包含’单元’(标准中使用的术语)中的最高有效位到最低有效位还是从最低到最高有效位是实现定义的。 最大允许位域的大小; 当一个位字段存储在一个新单元中时; 所有这些都是实现定义的。

例如,在使用GCC 4.8.1的Mac OS X 10.8.4上,可以certificate问题中的struct bitfield a占用3个最低有效位(位0-2), b占据下一个位来布局的。 13位(3-15), c占用下一个1位(16):

 #include  static void print_info(int v); int main(void) { int values[] = { 0x55555555, 0xAAAAAAAA, 0x87654321, 0xFEDCBA98, 0xFEDCBA90, 0xFEDCBA91, 0xFEDCBA92, 0xFEDCBA93, 0xFEDCBA94, 0xFEDCBA95, 0xFEDCBA96, 0xFEDCBA97, 0xFEDCBA98, 0xFEDCBAA0, 0xFEDCBAA8, 0x0000BAA0, 0x0001BAA0, 0x00000008, 0x00000010, 0x00000018, 0x0000FFF0, 0x0000FFF8, }; for (size_t i = 0; i < sizeof(values)/sizeof(values[0]); i++) print_info(values[i]); return 0; } static void print_info(int v) { union { unsigned int x; struct bitfield { signed int a:3; unsigned int b:13; unsigned int c:1; } y; } u; ux = v; printf("0x%.8X => %2d 0x%.4X %1X\n", ux, uya, uyb, uyc); } 

样本输出:

 0x55555555 => -3 0x0AAA 1 0xAAAAAAAA => 2 0x1555 0 0x87654321 => 1 0x0864 1 0xFEDCBA98 => 0 0x1753 0 0xFEDCBA90 => 0 0x1752 0 0xFEDCBA91 => 1 0x1752 0 0xFEDCBA92 => 2 0x1752 0 0xFEDCBA93 => 3 0x1752 0 0xFEDCBA94 => -4 0x1752 0 0xFEDCBA95 => -3 0x1752 0 0xFEDCBA96 => -2 0x1752 0 0xFEDCBA97 => -1 0x1752 0 0xFEDCBA98 => 0 0x1753 0 0xFEDCBAA0 => 0 0x1754 0 0xFEDCBAA8 => 0 0x1755 0 0x0000BAA0 => 0 0x1754 0 0x0001BAA0 => 0 0x1754 1 0x00000008 => 0 0x0001 0 0x00000010 => 0 0x0002 0 0x00000018 => 0 0x0003 0 0x0000FFF0 => 0 0x1FFE 0 0x0000FFF8 => 0 0x1FFF 0 

不是完全随机选择测试值。 从测试值0xFEDCBA90到0xFECBA97,我们可以看到最低有效3位包含a 。 从测试值0x0000BAA0和0x0001BAA0,我们可以看到第17位(或第16位)包含c 。 从测试值0x00000008到0x0000FFF8,我们可以看到位3-15包含b

但是,必须指出的是,该代码在理论上是可辩论的; 因为代码写入ux然后读取uxuyauybuyc ,它不会访问最后写入的union的成员,这是严格的未定义行为。 在实践中,它“总是”有效(我没有听说过一个它不起作用的系统 – 技术上不可能存在一个不起作用的系统)。

这种布局不是任何想象力的唯一可能布局。 但是,我无法访问演示替代布局的编译器或系统。


在ISO / IEC 9899:2011中,第6.7.2.1结构和联合说明符说:

¶11实现可以分配任何足够大的可寻址存储单元来保存位域。 如果剩余足够的空间,则紧跟在结构中的另一个位字段之后的位字段将被打包到相同单元的相邻位中。 如果剩余的空间不足,则是否将不适合的位域放入下一个单元或重叠相邻单元是实现定义的。 单元内的位域分配顺序(高阶到低阶或低阶到高阶)是实现定义的。 未指定可寻址存储单元的对齐。

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

126)未命名的位字段结构成员对于填充符合外部强加的布局很有用。

该问题中结构的一个略微变化是:

 struct exegesis { signed int a:3; unsigned int :0; unsigned int b:13; unsigned int :0; unsigned int c:1; }; 

此结构的大小为12(与之前的编译器/平台相同)。 此平台上位字段的存储单元为4个字节,因此匿名零宽度字段将启动一个新的存储单元。 a存储在第一个4字节单元的最低有效3位中; b在第二个4字节单元的最低有效13位中; 和c在第三个4字节单元的最低有效位。 如标准引用中所述,您也可以使用大于0的匿名位字段。

位字节如何存储在存储器中取决于实现。

一个可能的原因是,具有17位的位字段应至少保持3个字节,但编译器选择将其填充为4个字节。

同样,关于位字段的几乎所有内容都依赖于实现,包括它们的大小和内存布局。

最后,不要使用void main ,始终使用int main

如何存储位字段是建立依赖的装置,用于从左到右的小端和从右到左的大的enian。 存储器中的存储器发生在字大小上.1字节是1字。因此在我们的例子中,它的17位作为前两个8位将存储在前两个字中。下一位将存储在下一个字中。大小应该是3个字节,但编译器做了一些填充,所以最后大小变成4个字节。

对于内存中的存储,您可以参考此链接http://en.wikipedia.org/wiki/Endianness