为什么sizeof运算符返回的结构大小大于结构成员的总大小?


当前回答

在关于内存对齐和结构填充/打包的其他解释得很好的答案中,我通过仔细阅读问题本身发现了一些东西。

“为什么结构的sizeof不等于每个成员的sizeof之和?”“为什么sizeof运算符返回的结构大小大于结构成员的总大小”?

这两个问题都表明了一些明显的错误。至少在一般的、非示例性的视图中是这样的。

应用于结构对象的sizeof操作数的结果可以等于分别应用于每个成员的sizeof之和。它不一定要更大/不同。

如果没有填充的原因,则不会填充内存。


如果结构仅包含相同类型的成员,则大多数实现为:

struct foo {
   int a;   
   int b;
   int c;     
} bar;

假设sizeof(int)==4,结构杆的尺寸将等于所有构件的尺寸总和,sizeof(bar)==12。这里没有填充。

同样的例子如下:

struct foo {
   short int a;   
   short int b;
   int c;     
} bar;

假设sizeof(short int)==2,sizeof(int)==4。为a和b分配的字节之和等于为c分配的字节,c是最大的成员,因此所有内容都完全对齐。因此,sizeof(bar)==8。

这也是关于结构填充的第二个最受欢迎的问题的对象,这里:

C结构中的内存对齐

其他回答

如果隐式或显式设置了结构的对齐方式,则可以这样做。对齐为4的结构将始终是4字节的倍数,即使其成员的大小不是4字节的倍。

此外,一个库可以在x86下用32位int编译,并且您可以在64位进程上比较它的组件。如果您手动执行此操作,则会得到不同的结果。

结构的尺寸大于其部件的总和,这是因为所谓的填充。一个特定的处理器有一个首选的数据大小。大多数现代处理器的首选大小为32位(4字节)。当数据位于这种边界上时,访问内存比跨越这种大小边界的东西更有效。

例如考虑简单的结构:

struct myStruct
{
   int a;
   char b;
   int c;
} data;

如果机器是32位机器,并且数据在32位边界上对齐,我们会看到一个直接的问题(假设没有结构对齐)。在此示例中,让我们假设结构数据从地址1024开始(0x400-请注意,最低2位为零,因此数据与32位边界对齐)。对data.a的访问将正常工作,因为它从边界0x400开始。对data.b的访问也会很好,因为它位于地址0x404——另一个32位边界。但未对齐的结构将把data.c放在地址0x405。数据.c的4个字节位于0x405、0x406、0x407、0x408。在32位机器上,系统将在一个内存周期内读取data.c,但只能获得4个字节中的3个字节(第4个字节位于下一个边界)。因此,系统必须进行第二次内存访问以获得第四个字节,

现在,如果编译器不将data.c放在地址0x405,而是将结构填充3字节,并将data.c放到地址0x408,那么系统只需要1个周期就可以读取数据,从而将对该数据元素的访问时间减少50%。填充以内存效率换取处理效率。考虑到计算机可以拥有大量的内存(许多千兆字节),编译器认为这种交换(速度与大小)是合理的。

不幸的是,当您试图通过网络发送结构,甚至将二进制数据写入二进制文件时,这个问题就成了一个杀手。在结构或类的元素之间插入的填充会破坏发送到文件或网络的数据。为了编写可移植的代码(一个将被几个不同的编译器使用),您可能必须分别访问结构的每个元素,以确保正确的“打包”。

另一方面,不同的编译器有不同的能力来管理数据结构打包。例如,在Visual C/C++中,编译器支持#pragma pack命令。这将允许您调整数据打包和对齐。

例如:

#pragma pack 1
struct MyStruct
{
    int a;
    char b;
    int c;
    short d;
} myData;

I = sizeof(myData);

我现在的长度应该是11。如果没有pragma,我可以从11到14(对于某些系统,可以多达32),这取决于编译器的默认打包。

C语言为编译器提供了一些关于内存中结构元素位置的自由:

内存孔可能出现在任意两个组件之间以及最后一个组件之后。这是由于目标计算机上的某些类型的对象可能受到寻址边界的限制sizeof运算符的结果中包含“内存孔”大小。sizeof仅不包括灵活数组的大小,灵活数组在C/C中可用++该语言的一些实现允许您通过pragma和编译器选项控制结构的内存布局

C语言为程序员提供了结构中元素布局的一些保证:

编译器需要分配一系列增加内存地址的组件第一个组件的地址与结构的起始地址一致未命名的位字段可以包括在结构中,以实现相邻元素的所需地址对齐

与元素对齐相关的问题:

不同的计算机以不同的方式排列对象的边缘位字段宽度的不同限制计算机在如何存储一个字中的字节方面存在差异(Intel 80x86和Motorola 68000)

对齐方式:

结构所占据的体积被计算为这样的结构阵列中对齐的单个元素的大小。结构应以便下一个后续结构的第一个元素不违反对齐要求

p.s更多详细信息可在此处获得:“塞缪尔·p·哈比森,盖伊·L·斯蒂尔C A参考,(5.6.2-5.6.7)”

在关于内存对齐和结构填充/打包的其他解释得很好的答案中,我通过仔细阅读问题本身发现了一些东西。

“为什么结构的sizeof不等于每个成员的sizeof之和?”“为什么sizeof运算符返回的结构大小大于结构成员的总大小”?

这两个问题都表明了一些明显的错误。至少在一般的、非示例性的视图中是这样的。

应用于结构对象的sizeof操作数的结果可以等于分别应用于每个成员的sizeof之和。它不一定要更大/不同。

如果没有填充的原因,则不会填充内存。


如果结构仅包含相同类型的成员,则大多数实现为:

struct foo {
   int a;   
   int b;
   int c;     
} bar;

假设sizeof(int)==4,结构杆的尺寸将等于所有构件的尺寸总和,sizeof(bar)==12。这里没有填充。

同样的例子如下:

struct foo {
   short int a;   
   short int b;
   int c;     
} bar;

假设sizeof(short int)==2,sizeof(int)==4。为a和b分配的字节之和等于为c分配的字节,c是最大的成员,因此所有内容都完全对齐。因此,sizeof(bar)==8。

这也是关于结构填充的第二个最受欢迎的问题的对象,这里:

C结构中的内存对齐

C99 N1256标准草案

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

6.5.3.4操作员尺寸:

3当应用于具有结构或并集类型的操作数时,结果是这样的对象中的字节总数,包括内部和尾部衬垫。

6.7.2.1结构和接头规范:

13 ... 可能有未命名的在结构对象内填充,但不能在其开头填充。

and:

15在结构或联合体的末端可能有未命名的填充。

新的C99灵活数组成员特性(结构S{int is[];};)也可能影响填充:

16作为一种特殊情况,具有多个命名构件的结构的最后一个元素可以具有不完整的数组类型;这称为灵活阵列成员。在大多数情况下,柔性阵列成员被忽略。特别是,结构的大小似乎省略了灵活的数组成员,除了它可能具有比这一遗漏意味着。

附件J便携性问题重申:

以下未指定:。。。在结构或联合中存储值时填充字节的值(6.2.6.1)

C++11 N3337标准草案

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

5.3.3尺寸:

2应用时对于类,结果是该类的对象中的字节数,包括将该类型的对象放置在数组中。

9.2班级成员:

指向标准布局结构对象的指针(使用reinterpret_cast进行适当转换)指向其初始成员(或者如果该成员是位字段,则返回到它所在的单元),反之亦然。[注:因此,在标准布局结构对象中可能存在未命名的填充,必要时实现适当的对齐。-尾注]

我只知道足够的C++来理解注释:-)