结构体对齐主要是为了提高处理器读取内存的效率。举个例子来说,假设一个处理器每次读写内存都是从4字节的倍数处读写,并且每次能读写4字节的数据,如果一个int型数据起始地址在4字节倍数处,那么一次CPU操作就可以取出这个int型数据,否则至少要进行两次读取操作,并需要进行位操作才能拼凑出这个int型数据。所以,对于包含多个成员变量的结构体来说,要在不同的变量之间填充空白字节以使每个字段的首字节保持对齐,也就是所谓的结构体对齐。可见,结构体对齐是一种以空间换时间的优化方式。
需要注意的是,在Linux与Windows之间,32位OS和64位OS之间,甚至GCC和VC++之间,结构体的对齐方式都是有区别的,所以在不同环境下 sizeof(struct)
操作得到的结果经常是不同的,本文的测试环境基于32位Ubuntu,GCC4.7。
首先,为了方便阐述,将对齐值分为以下几种:
1)基本数据自身对齐值:基本数据类型的自身对齐值,一般由编译器及系统决定;
2)结构体自身对齐值:取决于结构体成员中自身对齐值的最大值;
3)指定对齐值:通过 #pragma pack(n)
宏指定的对齐值;
4)有效对齐值:取决于自身对齐值和指定对齐值中的较小值。
对于每种基本数据类型,都有一个自身对齐值, 而结构体的自身对齐值将取成员中自身对齐值最大的
32位OS下Linux/GCC与Windwos/VC++中基本数据类型自身对齐值(来自Wiki)
类型 | Windows/VC++ | Linux/GCC | 长度 | 对齐值 | 长度 | 对齐值 | char | 1 | 1 | 1 | 1 | short | 2 | 2 | 2 | 2 | int,long,float,pointer | 4 | 4 | 4 | 4 | double | 8 | 8 | 8 | 4 | long double | 8 | 8 | 12 | 4 | long long | 8 | 8 | 8 | 8 |
以下几种基本数据类型在64位OS下自身对齐值有所不同
类型 | Windows/VC++ | Linux/GCC | 长度 | 对齐值 | 长度 | 对齐值 | long | 8 | 8 | 8 | 8 | double | 8 | 8 | 8 | 8 | long double | 8 | 8 | 16 | 16 | pointer | 8 | 8 | 8 | 8 |
另外,有些编译器可以通过 #pragma pack(n)
宏来指定结构体的对齐方式,n的取值范围是{1,2,4,8,16},(GCC中默认是4,VC++中默认是8) 基本数据类型的有效对齐值将取n和自身对齐值中较小的,而结构体的有效对齐值将取决于n和结构体自身对齐值中较小的 ,也就是说如果前面算出来结构体的自身对齐值是4,那么指定n为8或者16都是没用的。
结构体大小的计算方法遵循以下两条规则:
1)假设结构体的起始位置为0,结束位置为n,n必须是结构体有效对齐值的倍数。
2)假设某成员的起始对齐值是m,m必须是该成员有效对齐值的倍数。
typedef struct _C{
int a;
char b;
int* c;
short d;
}C;
首先,int,char,pointer,short的自身对齐值分别为4,1,4,2,所以结构体自身对齐值为4。
a起始位置0,结束位置4,0%4=0,占4个字节。
b起始位置4,结束位置5,4%1=0,占1个字节。
c起始位置8,结束位置12,8%4=0,占4个字节,需要在前面填充3个字节。
d起始位置12,结束位置14,12%4=0,占2个字节。
最后结构体结束位置16,16%4=0,需要在d后填充2个字节,结构体共占16个字节。
#pragma pack(2)
typedef struct _C{
int a;
char b;
int *c
short d;
}C;
该结构体与样例一中相同,不同的是指定了对齐值为2,那么int,char,pointer,short的有效对齐值变为2,1,2,2,结构体有效对齐值为2。
a起始位置0,结束位置4,0%2=0,占4个字节。
b起始位置4,结束位置5,4%1=0,占1个字节。
c起始位置6,结束位置10,6%2=0,占4个字节,需要在前面填充1个字节。
d起始位置10,结束位置12,12%4=2,占2个字节。
最后结构体结束位置12,12%2=0,结构体共占12个字节。
typedef struct _C{
int a;
int *b;
short c;
char d;
}C;
该结构体与样例一中相同,只是更改了成员的排列方式。
a起始位置0,结束位置4,0%4=0,占4个字节。
b起始位置4,结束位置8,4%4=0,占4个字节。
c起始位置8,结束位置10,8%2=0,占2个字节。
d起始位置10,结束位置11,10%1=0,占1个字节。
最后结构体结束位置12,12%4=0,需要在d后填充1个字节,结构体共占12个字节。
可以看到,通过更改结构体成员的排列方式,可以节省内存空间,所以往往要精心设计结构体排列来达到节省内存的目的。
http://blog.csdn.net/han_xiaoyang/article/details/11596001
http://en.wikipedia.org/wiki/Data_structure_alignment