现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
(1)现象
比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。看下面一个例子:
32位机上,默认4字节对齐
struct A
{
int a;
char b;
short c;
};
struct B
{
char b;
int a;
short c;
};
main()
{
struct A x;
struct B y;
printf("%d %d", sizeof(x),sizeof(y));
}
得到的结果是8,12。按理说两者应该一样是7才对?之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐。对A,a占4字节,b占一字节,c占两字节,b、c可以放在一个4字节里面,且最小分配是4,所以共占8;对B,b占一字节,但它容纳不了a了,所以a要占另外4字节,b只能单占4字节,后面的c也只能单占4字节,也即没有共用的,所以占12。可见次序是有影响的。
上面是按照编译器的默认设置进行对齐的结果,那么我们是不是可以改变编译器的这种默认对齐设置呢?当然可以。
#pragma pack(2)
struct B
{
char b;
int a;
short c;
};
int _tmain()
{
B y;
printf("%d ",sizeof(y));
}
输出是8,因为是2字节对齐最小可以2字节分配,b占两字节,a占4字节,c占2字节。类似的还有如下:
#pragma pack(1)
struct B
{
char b;
int a;
short c;
};
int _tmain()
{
B y;
printf("%d ",sizeof(y));
}
输出是7,因为1字节对齐,最小可以分配1。那么b占一字节,a占4字节,c占2字节。
(2)原则归纳
综合如下,对齐的原则:
1,数据类型自身的对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。
2,结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
3,指定对齐值:#pragma pack (value)时的指定对齐值value。
(3)如何使用
如何修改编译器的默认对齐值?
1,在VC IDE中,可以这样修改:[Project]|[Settings],c/c++选项卡Category的Code Generation选项的Struct Member Alignment中修改,默认是8字节。
2,在编码时,可以这样动态修改:#pragma pack 。
3,如果在编程的时候要考虑节约空间的话,那么我们只需要假定结构的首地址是0,然后各个变量按照上面的原则进行排列即可。基本的原则就是把结构中的变量按照类型大小从小到大声明,尽量减少中间的填补空间。还有一种就是为了以空间换取时间的效率,比如:有一种使用空间换时间做法是显式的插入reserved成员:
struct A{
char a;
char reserved[3];//使用空间换时间
int b;
}
reserved成员对我们的程序没有什么意义,它只是起到填补空间以达到字节对齐的目的,当然即使不加这个成员通常编译器也会给我们自动填补对齐,我们自己加上它只是起到显式的提醒作用。
转自http://blog.youkuaiyun.com/sky0829/article/details/6010482