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个字节,因此在v3
和i
之间插入了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方式(地址以
0xC
或0xC
结尾)。 请注意,这也适用于大于4个字节的types(例如double
和long double
)。 - 32位Windows更为严格,因为如果一个types的大小为K字节,则它必须是K字节alignment的。 这意味着
double
只能放在地址为0x0
或0x8
的地址。 唯一的例外是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,以便在v3
和i
之间插入填充以alignmenti
。
第二个sizeof(myStruct)
为12
是在v3
和i
之间插入的填充,以便在32位边界处alignmenti
。 有两个字节。
维基百科明确地解释了填充和alignment。
这个标准并没有提到完整types结构的布局 – 这取决于编译器。 它决定它需要int开始在一个边界上来访问它,但是由于它必须为短裤做边界内存寻址,所以不需要填充它们
听起来像是根据每个var的大小alignment到bounder,所以地址是被访问大小的倍数(所以shortsalignment到2,intsalignment到4等),如果你移动了之后的短裤int, sizeof(mystruct)
应该是10.当然,这一切都取决于正在使用的编译器以及它使用的设置。