Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C—使用位字段时结构的大小以及它在内存中的存储方式_C_Struct - Fatal编程技术网

C—使用位字段时结构的大小以及它在内存中的存储方式

C—使用位字段时结构的大小以及它在内存中的存储方式,c,struct,C,Struct,为什么这里的大小是4字节?这些元素究竟如何存储在内存中?位字段如何存储在内存中取决于实现 一个可能的原因是,具有17位的位字段应至少包含3个字节,但编译器选择将其填充为4个字节 同样,几乎所有关于位字段的内容都依赖于实现,包括它们的大小和内存布局 最后,不要使用void main,始终使用int main几乎每个位字段都是实现定义的。甚至“plainint”位字段的符号性也是实现定义的;它可以是有符号的,也可以是无符号的。字段的布局——它们是从包含“单元”(标准中使用的术语)中的最高有效位到最低

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

位字段如何存储在内存中取决于实现

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

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


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

几乎每个位字段都是实现定义的。甚至“plain
int
”位字段的符号性也是实现定义的;它可以是有符号的,也可以是无符号的。字段的布局——它们是从包含“单元”(标准中使用的术语)中的最高有效位到最低有效位,还是从最低有效位到最高有效位,由实现定义。最大允许位字段的大小;在新单元中存储位字段时;所有这些都是实现定义的

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

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

然而,必须指出,该代码在理论上是可移植的,这是有争议的;由于代码先写入
u.x
,然后读取
u.x
u.y.a
u.y.b
u.y.c
,因此它不会访问上次写入的联盟成员,这是严格未定义的行为。在实践中,它“总是”起作用(我从来没有听说过一个系统不起作用——不太可能有一个系统不起作用,但在技术上并非不可能)

这种布局并不是想象中唯一可能的布局。但是,我无法访问演示其他布局的编译器或系统


在ISO/IEC 9899:2011中,第6.7.2.1节“结构和联合规范”中规定:

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

^12没有声明符但只有冒号和宽度的位字段声明表示未命名的位字段。126)作为特例,宽度为0的位字段结构成员表示没有更多的位字段要打包到放置前一位字段(如果有)的单元中

126)未命名的位字段结构成员用于填充以符合外部施加的布局

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

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

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

位字段的存储方式取决于架构,即从左到右的小端点和从右到左的大端点。 内存中的存储与单词大小有关。1字节是1个单词。因此,在我们的示例中,它的17位作为前两个8位将存储在前两个单词中。下一位将存储在下一个单词中。大小应为3字节,但编译器会进行一些填充,因此最后大小将变为4字节

有关内存中的存储,请参考此链接

可能重复的请不要使用
void main()
。C标准和微软都不认可这种用法,尽管许多书都提出了不同的建议。结构填充和打包上的“可能重复”不涉及位字段,位字段有自己的规则集,这些规则集的差异足以使答案不适合作为重复项。可能还有其他复制品,但不是那个。@JonathanLeffler+1。然而,我相信在一些针对嵌入式系统的编译器上(这是现在人们唯一使用位域的地方,
main()
不会返回值-尽管我忘记了我在哪里读到的。@ldrumm:这是一个全面的陈述(尽管它的内核是真的)表示位字段仅用于嵌入式系统。如果您是为嵌入式系统编码,最好这样说,以避免被指控错误定义
main()
。在没有此类标识的情况下,这是一种(令人沮丧的普遍现象)错误。
a
b
的数据很可能在同一个16位单元中;但是
c
的第17位必须放在某个地方。虽然结构可以有3个字节长,但编译器选择将其设为4个字节。因为它是一个i
#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
struct exegesis
{
    signed int   a:3;
    unsigned int  :0;
    unsigned int b:13;
    unsigned int  :0;
    unsigned int c:1;
};