C结构中的内存alignment

我在32位机器上工作,所以我想内存alignment应该是4个字节。 说我有结构:

typedef struct { unsigned short v1; unsigned short v2; unsigned short v3; } myStruct; 

真正的大小是6个字节,我想alignment的大小应该是8,但sizeof(myStruct)返回给我6。

但是,如果我写:

 typedef struct { unsigned short v1; unsigned short v2; unsigned short v3; int i; } myStruct; 

实际大小是10个字节,alignment是12,这次sizeof(myStruct) == 12

有人可以解释有什么区别?

至less在大多数机器上,一种types只能与types本身alignment的边界alignment[编辑:你不能真正要求任何“更多”的alignment,因为你必须能够创build数组,而你不能将填充插入数组]。 在你的实现中, short显然是2个字节, int 4个字节。

这意味着你的第一个结构是alignment到一个2字节的边界。 由于所有成员都是2个字节,所以它们之间没有填充。

第二个包含一个4字节的项目,它alignment到一个4字节的边界。 由于前面是6个字节,因此在v3i之间插入了2个字节的填充字符,在v3给出了6个字节的数据,两个字节的填充和另外4个字节的数据在总共12个字节中。

忘记拥有不同的成员,即使你写了两个成员完全相同的结构体, 不同之处在于它们声明的顺序是不同的,那么每个结构体的大小可以是(通常是)不同的。

例如,看到这个,

 #include <iostream> using namespace std; struct A { char c; char d; int i; }; struct B { char c; int i; //note the order is different! char d; }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; } 

编译它与gcc-4.3.4 ,你得到这个输出:

 8 12 

即使两个结构都有相同的成员,大小也是不同的!

代码在Ideone: http ://ideone.com/HGGVl

底线是标准不会讨论如何进行填充,所以编译器可以自由地做出任何决定,而且不能假定所有的编译器都做出相同的决定。

默认情况下,值根据其大小进行alignment。 因此,一个2字节的值就像一个short字符在2字节的边界上alignment,4字节的值如int在4字节的边界上alignment

在你的例子中,在i之前添加2个填充字节,以确保i落在4字节的边界上。

(整个结构在边界上alignment,至less与结构中最大的值一样大,所以你的结构将alignment到一个4字节的边界。)

实际的规则根据平台的不同而不同 – 维基百科页面上的数据结构alignment有更多的细节。

编译器通常允许您通过(例如) #pragma pack指令来控制打包。

假设:

 sizeof(unsigned short) == 2 sizeof(int) == 4 

那么我个人会使用以下(你的编译器可能会有所不同):

 unsigned shorts are aligned to 2 byte boundaries int will be aligned to 4 byte boundaries. typedef struct { unsigned short v1; // 0 bytes offset unsigned short v2; // 2 bytes offset unsigned short v3; // 4 bytes offset } myStruct; // End 6 bytes. // No part is required to align tighter than 2 bytes. // So whole structure can be 2 byte aligned. typedef struct { unsigned short v1; // 0 bytes offset unsigned short v2; // 2 bytes offset unsigned short v3; // 4 bytes offset /// Padding // 6-7 padding (so i is 4 byte aligned int i; // 8 bytes offset } myStruct; // End 12 bytes // Whole structure needs to be 4 byte aligned. // So that i is correctly aligned. 

首先,虽然填充的细节留给编译器,但操作系统也对alignment要求施加了一些规则。 这个答案假定你使用的是gcc,但操作系统可能会有所不同

要确定给定结构及其元素占用的空间,可以遵循以下规则:

首先,假定结构总是从一个适合所有数据types的地址开始。

然后,对于结构中的每个条目:

  • 所需的最小空间是由sizeof(element)给出的元素的原始大小。
  • 元素的alignment要求是元素的基本types的alignment要求。 值得注意的是,这意味着char[20]数组的alignment要求与简单char的要求相同。

最后,整个结构的alignment要求是每个元素的alignment要求的最大值。

gcc会在给定的元素之后插入填充,以确保下一个(或者说,如果我们正在讨论最后一个元素的话)正确alignment。 它不会重新排列结构中元素的顺序,即使这样可以节省内存。

现在alignment要求本身也有点奇怪。

  • 32位Linux要求2字节数据types具有2字节alignment(它们的地址必须是偶数)。 所有较大的数据types都必须有4字节alignment方式(地址以0xC0xC结尾)。 请注意,这也适用于大于4个字节的types(例如doublelong double )。
  • 32位Windows更为严格,因为如果一个types的大小为K字节,则它必须是K字节alignment的。 这意味着double只能放在地址为0x00x8的地址。 唯一的例外是long double ,即使它实际上是12个字节,仍然是4字节alignment的。
  • 对于Linux和Windows,在64位机器上,K字节types必须与K字节alignment。 再一次, long double是一个例外,必须是16字节alignment的。

每个数据types需要在自己的大小的内存边界上alignment。 所以需要在一个2字节的边界上alignment一个short int ,并且一个int需要在一个4字节的边界上。 同样的, long long需要在一个8字节的边界上。

在你的第一个结构中,由于每个项目的大小都很short ,整个结构可以在short边界上alignment,所以不需要在结尾添加任何填充。

在第二个结构中,int(可能是32位)需要进行字alignment,以便在v3i之间插入填充以alignmenti

第二个sizeof(myStruct)12是在v3i之间插入的填充,以便在32位边界处alignmenti 。 有两个字节。

维基百科明确地解释了填充和alignment。

这个标准并没有提到完整types结构的布局 – 这取决于编译器。 它决定它需要int开始在一个边界上来访问它,但是由于它必须为短裤做边界内存寻址,所以不需要填充它们

听起来像是根据每个var的大小alignment到bounder,所以地址是被访问大小的倍数(所以shortsalignment到2,intsalignment到4等),如果你移动了之后的短裤int, sizeof(mystruct)应该是10.当然,这一切都取决于正在使用的编译器以及它使用的设置。