内存对齐是操作系统为了快速访问内存而采取的一种策略,简单来说,就是为了防止变量的二次访问。操作系统在访问内存时,每次读取一定的长度(操作系统的默认对齐系数,一般是4或8,或者它的整数倍)。如果没有内存对齐,为了读取一个变量,会产生总线的二次访问!
举个栗子,
struct xx{
char b; //0xffbff5e8
int a; //0xffbff5e9
int c; //0xffbff5ed
char d; //0xffbff5f1
};
操作系统先读取0xffbff5e8-0xffbff5ef的内存,再读取0xffbff5f0-0xffbff5f8的内存,这样,因为c的存储空间横跨这2组内存,要得到它的值,就需要将这两组内存合并,进行整合,这样严重降低了内存的访问效率。
这样就能理解为什么结构体的第一个变量,不管类型如何,都是能被8整除的(因为访问内存是从8的整数倍开始的,为了增加读取的效率)!
下面说说各种数据类型的首地址吧,就讲windows上的吧,先。
内存对齐的目的是使各个基本数据类型的首地址为对应K的倍数!
每个基本类型的大小即为这个K,char = 1,int = 4,long = 4,double = 8
举个栗子,
struct test1
{
char a; //0 //1被填充
short b; //2,3
int c; //4,5,6,7
long d; //8,9,10,11 //12-15被填充
double e;//16-23
};
假设从0地址开始,首先a的K值为1,它的首地址可以是任意位置,所以a占用第一个字节,即地址0;然后b的K值为2,它的首地址必须是2的倍数,不能是1,所以地址1的那个字节被填充,b的首地址为地址2,占用地址2,3;然后到c,c的K值为4,它的首地址为4的倍数,所以首地址为4,占用地址4,5,6,7;然后到d,d的K值也为4,所以它的首地址为8,占用地址8,9,10,11.最后到e,它的K值为8,首地址为8的倍数,所以地址12,13,14,15被填充,它的首地址应为16,占用地址16-23.显然其大小为24。
关于结构体里再加结构体的话,得看里面结构体里最大的那个类型,比如
struct test2
{
char f ;
struct test1 g;
}
sizeof(test2) = sizeof(test1)+8;//因为test1结构体里,double最大为8,如果没有double,只有int或long就是+4了
另外,结构体里有enum,就是4,有static 就是0,因为sizeof不统计静态存储。
还有一种特殊的---位段对齐
struct test3
{
unsigned int a:4;
unsigned int b:4;
char c;
};
或者
struct test3
{
unsigned int a:4;
int b:4;
char c;
};
sizeof(test3) = 8;//相邻的多个同类型的数(带符号的与不带符号的,只要基本类型相同,也为相同的数),以上,a和b可作为一个整体,a与b分别位于4字节区域中的前0-3位和4-7位。
struct test4
{
unsigned int a:30;
unsigned int b:4;
char c;
};
sizeof(test4) = 12;//a与b分别分布在第一个4字节的前30位,和第二个4字节的前4位。
struct test5
{
unsigned int a:4;
unsigned char b:4;
char c;
};
sizeof(test5) = 8;//因为int和char不同类型,a和b的值分别位于第一个4字节的前4位,和第5个字节的前4位。
参考网址:http://www.jb51.net/article/44221.htm