结构填充和包装

考虑:

struct mystruct_A { char a; int b; char c; } x; struct mystruct_B { int b; char a; } y; 

结构的大小分别是12和8。

这些结构是填充还是包装?

填充或包装什么时候发生?

填充 将结构成员alignment到“自然”地址边界 – 比方说, int成员将有偏移量,在32位平台上是mod(4) == 0 。 填充默认打开。 它将下面的“空白”插入到您的第一个结构中:

 struct mystruct_A { char a; char gap_0[3]; /* inserted by compiler: for alignment of b */ int b; char c; char gap_1[3]; /* -"-: for alignment of the whole struct in an array */ } x; 

包装 ,另一方面防止编译器做填充 – 这必须明确要求 – 在GCC下它是__attribute__((__packed__)) ,所以下面:

 struct __attribute__((__packed__)) mystruct_A { char a; int b; char c; }; 

会在32位架构上产生大小为6结构。

尽pipe如此,未alignment的内存访问在允许它的体系结构(如x86和amd64)上运行速度较慢,而且在严格的alignment体系结构(如SPARC)中明确禁止。

结构填充抑制结构填充,alignment最重要时使用的填充,空间最重要的填充。

一些编译器提供了#pragma来压缩填充或使它打包成n个字节。 一些提供关键字来做到这一点。 通常,用于修改结构填充的编译指示将采用以下格式(取决于编译器):

 #pragma pack(n) 

例如,ARM提供__packed关键字来抑制结构填充。 通过你的编译器手册来了解更多关于这个。

所以打包结构是没有填充的结构。

通常使用包装结构

  • 节省空间

  • 格式化一个数据结构,通过networking使用某种协议进行传输(这当然不是一个好的做法,因为您需要
    处理sorting)

我知道这个问题已经很老了,这里的大多数答案都很好地解释了填充问题,但是在尝试自己理解的时候,我想到了一个“可视化”的图像。

处理器以一定大小(字)的“块”读取存储器。 说处理器字是8字节长。 它将把内存视为一大块8字节的构build块。 每次需要从内存中获取一些信息,它会到达其中一个块并获得它。

变量对齐

如上图所示,char(1字节长)的位置并不重要,因为它将位于其中一个块内,要求CPU只处理1个字。

当我们处理大于一个字节的数据时,例如4字节的int或8字节的double,它们在内存中alignment的方式会影响CPU处理多less个字。 如果4字节块以某种方式alignment,它们总是适合块内部(内存地址是4的倍数),则只需要处理一个字。 否则,一个4字节的块可能在一个块上有一部分,另一部分在另一个块上,需要处理器处理2个字来读取这个数据。

这同样适用于一个8字节的double,除了现在它必须在8的倍数的内存地址,以保证它总是在一个块内。

这考虑了一个8字节的文字处理器,但是这个概念适用于其他尺寸的文字。

填充工作是通过填充这些数据之间的间隙来确保它们与这些块alignment,从而在读取存储器的同时提高性能。

然而,正如其他人所说的那样,有时候空间本身就更重要。 也许你正在一个没有太多内存的计算机上处​​理大量的数据(可以使用交换空间,但是速度很慢)。 你可以在程序中安排variables,直到完成最less的填充(因为在其他一些答案中已经有很多例子),但是如果这还不够,你可以明确地禁用填充,这就是填充

上面的答案很清楚地解释了原因,但是填充的大小似乎并不完全清楚,所以我会根据我从“C结构包装的失落艺术”中学到的东西来加一个答案

内存alignment规则 – 对于结构

  • 在每个成员之前,都会有填充符号,以便在可以被大小整除的地址处开始填充。 例如在64位系统上, int应该在可被4整除的地址处开始,并且长8乘以2。
  • char和char []是特殊的,可以是任何内存地址,所以它们不需要填充。
  • 对于struct ,除了每个成员的alignment需求之外,整个结构本身的大小将按最大个体成员的大小alignment到大小divisale,最后填充。 例如,如果struct的最大成员是长的,那么可以被8整除,int则被4整除,然后被2整除。

对于64位系统

memory_align.c

 /** * Memory align & padding - for struct. * compile: gcc memory_align.c * execute: ./a.out */ #include <stdio.h> // size is 8, 4 + 1, then round to multiple of 4 (int's size), struct stu_a { int i; char c; }; // size is 16, 8 + 1, then round to multiple of 8 (long's size), struct stu_b { long l; char c; }; // size is 24, l need padding by 4 before it, then round to multiple of 8 (long's size), struct stu_c { int i; long l; char c; }; // size is 16, 8 + 4 + 1, then round to multiple of 8 (long's size), struct stu_d { long l; int i; char c; }; int test() { printf("%s: %ld\n", "stu_a", sizeof(struct stu_a)); printf("%s: %ld\n", "stu_b", sizeof(struct stu_b)); printf("%s: %ld\n", "stu_c", sizeof(struct stu_c)); printf("%s: %ld\n", "stu_d", sizeof(struct stu_d)); return 0; } int main(int argc, char * argv[]) { test(); return 0; } 

执行结果:

 stu_a: 8 stu_b: 16 stu_c: 24 stu_d: 16 

提示

  • 成员的顺序可能会影响结构的实际大小,所以要记住这一点。 例如上例中的stu_cstu_d具有相同的成员,但顺序不同,并且导致结构的大小不同。

填充和包装只是同一件事的两个方面:

  • 包装或alignment是每个成员四舍五入的大小
  • 填充是为了匹配alignment而添加的额外空间

mystruct_A ,假定默认alignment方式为4,则每个成员alignment在4个字节的倍数上。 由于char的大小是1,对ac的填充是4 – 1 = 3个字节,而对于已经是4个字节的int b不需要填充。 它与mystruct_B工作方式相同。

结构填充是在结构的末尾添加额外的位,以便结构完成字边界。 在结构填充中,您会发现更多关于避免结构填充的内容。

结构打包只有在你明确地告诉你的编译器打包结构时才能完成。 填充是你所看到的。 您的32位系统填充每个字段以进行字alignment。 如果你已经告诉你的编译器打包结构,它们分别是6和5个字节。 不要那样做。 这是不可移植的,并使编译器生成更慢(有时甚至是越野车)的代码。

数据结构alignment是数据在计算机内存中的排列和访问方式。 它由两个独立但相关的问题组成:数据alignment和数据结构填充 。 当现代计算机读取或写入存储器地址时,它将以字大小的块(例如,32位系统上的4字节块)或更大的块执行此操作。 数据alignment意味着将数据放在一个内存地址等于字长的几倍的地方,由于CPU处理内存的方式而增加了系统的性能。 要alignment数据,可能需要在最后一个数据结构的末尾和下一个数据结构填充的开始之间插入一些无意义的字节。

  1. 为了alignment内存中的数据,在内存分配时为其他结构成员分配的内存地址之间插入(或留空)一个或多个空字节(地址)。 这个概念被称为结构填充。
  2. 计算机处理器的体系结构是这样一种方式,一次可以从存储器中读取1个字(32位处理器中的4个字节)。
  3. 为了利用处理器的这个优点,数据总是以4个字节的数据包alignment,这导致在其他成员的地址之间插入空地址。
  4. 由于C中的这种结构填充概念,结构的大小总是和我们想象的不一样。