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

2023-11-29

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字节?这些元素到底是如何存储在内存中的?


位字段的几乎每个方面都是由实现定义的。即使是“普通”的符号int' 位域是实现定义的;它可以是已签名的或未签名的。字段的布局 - 无论它们是从包含“单元”(标准中使用的术语)中的最高有效位到最低有效位,还是从最低有效位到最高有效位,都是由实现定义的。最大允许位域的大小;当位字段存储在新单元中时;所有这些都是实现定义的。

例如,在使用 GCC 4.8.1 的 Mac OS X 10.8.4 上,可以证明struct bitfield问题中列出了a占据 3 个最低有效位(位 0-2),b占用接下来的 13 位 (3-15),并且c占用接下来的 1 位 (16):

#include <stdio.h>

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;
    u.x = v;
    printf("0x%.8X => %2d 0x%.4X %1X\n", u.x, u.y.a, u.y.b, u.y.c);
}

示例输出:

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.

然而,必须指出的是,理论上该代码的可移植性值得商榷。因为代码写入u.x然后读u.x and u.y.a, u.y.b and u.y.c,它没有访问最后写入的联合成员,这是严格未定义的行为。在实践中,它“总是”有效(我还没有听说过有一个系统不能工作——不太可能存在它不能工作的系统,但在技术上并非不可能)。

不管怎样,这种布局并不是唯一可能的布局。但是,我无法访问演示替代布局的编译器或系统。


在 ISO/IEC 9899:2011 中,部分§6.7.2.1 结构和联合说明符 says:

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

¶12 A bit-field declaration with no declarator, but only a colon and a width, indicates an unnamed bit-field.126) As a special case, a bit-field structure member with a width of 0 indicates that no further bit-field is to be packed into the unit in which the previous bitfield, if any, was placed.

126) An unnamed bit-field structure member is useful for padding to conform to externally imposed layouts.

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

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 的匿名位字段。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C- 使用位域时结构的大小以及它如何存储在内存中 的相关文章

随机推荐