内存对齐的小总结

来源:百度文库 编辑:神马文学网 时间:2024/04/27 16:26:50

理论上讲访问内存可以从任意地址开始,但考虑到访问效率,通常是从特定的内存地址访问。一些平台对某些特定类型的数据只能从一些特定地址开始访问,比如一个平台每次访问都从偶地址开始,一个int型(32位)数据如果存放在偶地址开始的地方,那么一个读周期就能读出;如果存放在奇地址开始的地方,就需要两个读周期,并对两次读取的高低字节进行拼凑才能得到该int型数据,读取效率上下降很多。

通常,我们编写程序时不用考虑对齐的问题,编译器会自动替我们选好对其策略,然后这也造成了一些困惑,如sizeof()函数取值时,会得到一些意想不到的结果。
几个对齐值的概念:
自身对齐值:            数据类型自身的对齐值,如int,char,double等;              结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值;指令对齐值:使用预编译指令#pragma pack(value)来告诉编译器我们指定的对齐值来取代缺省的自身对齐值;有效对齐值:数据成员、结构体和类的有效对齐值是指自身对齐值和指定对其之中较小的那个值。
如果没有指定对齐值,有效对齐值 = 自身对齐值;
若指定了对齐值,有效对齐值 = Min(自身对齐值,指定对齐值);
编译器最终按照有效长度来进行对齐。

编写了一个简单的测试程序,观察结构体各元素的地址; 
#include

struct A{
 char a;
 int b;
 short c;
};

struct B{
 int a;
 char b;
 short c;
};

#pragma pack(2) //指定对齐值长度为2
struct C{
 char a;
 int b;
 short c;
};
#pragma pack()

int main()
{
 A test1;
 B test2;
 C test3;
 printf("A length is %d\n",sizeof(test1));//长度为12
 printf("struct A address %p:\n",&test1);//A的起始地址为0012ff68,
 printf("a of A address %p:\n",&(test1.a));//0012ff68
 printf("b of A address %p:\n",&(test1.b));//0012ff6c
 printf("c of A address %p:\n",&(test1.c));//0012ff70
test1从地址0012ff68开始存储,test1.a自身长度为1,0012ff68%1 == 0,即占用12ff68这一个字节;
接下来存储test1.b,起始地址必须要4字节整除,从12ff68开始往后增加找到12ff6c,12ff6c%4 == 0,因此确定该值为test1.b的首地址,test1.b占用12ff6c~12ff6f四字节空间。此时12ff69~12ff6B之间的内存为未使用的空间。
test1.c为2字节对齐,由于12ff70%2 == 0,确定该地址为起始地址。占用12ff70~12ff71两字节空间。
此时结构体的变量共占用从12ff68~12ff71的10字节空间,由于总空间的长度必须要能被结构体的有效长度4整除,因此结构体的长度为12. printf("B length is %d\n",sizeof(test2));//8
 printf("a of B address %p:\n",&(test2.a));//0012ff58
 printf("b of B address %p:\n",&(test2.b));//0012ff5C
 printf("c of B address %p:\n",&(test2.c));//0012ff5E分配的首地址12ff58刚好整除4,12ff58~12ff5B用来存储test2.a;test2.b占一个字节,用12ff5C来存储;test2.c要两字节对齐,从12ff5c往后增加,寻找到第一个被2整除的地址12ff5e,以该地址作为test2.c的起始地址;因此test2结构体中的数据变量共占用4+2+2空间,而有效长度为4,8%4==  0,结构体本身不需在圆整; printf("C length is %d\n",sizeof(test3));//8
 printf("a of C address %p:\n",&(test3.a));//0012ff60
 printf("b of C address %p:\n",&(test3.b));//0012ff62
 printf("c of C address %p:\n",&(test3.c));//0012ff66
指定对齐值为2,除12ff60外,往后递增找到第一个能整除2的地址来作为test3.b的起始地址,12ff62%2 == 0,占用4个字节;12ff66%2 == 0,该地址作为short c的起始地址。现数据成员占用的总空间为2+4+2 = 8,能被有效长度2整除,不需再圆整,因此test3的大小为8。
 return 0;
}