1.什么是内存对齐?
现代计算机中内存空间都是按照字节(byte)进行划分的,所以从理论上讲对于任何类型的变量访问都可以从任意地址开始,但是在实际情况中,在访问特定类型变量的时候经常在特定的内存地址访问,所以这就需要把各种类型数据按照一定的规则在空间上排列,而不是按照顺序一个接一个的排放,这种就称为内存对齐。
2.为什么要内存对齐?
平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
3.对齐系数
编译器规定的内存数据访问的规则,每个编译器都有默认的对齐系数,一般64位编译器默认对齐系数为8,32位编译器默认对齐系数为4。如果默认的对齐系数为4,那么计算机将从起始地址开始,每次访问四个字节数据,然后将起始地址偏移四个字节,继续访问后面的数据。
4.对齐规则
- 在一个结构体中,若所有成员变量的数据类型的大小都小于预规定的对齐系数时,编译器会自动选择占用字节数最大的数据类型作为对齐系数。
- 如果有数据类型大小大于或等于预规定的对齐系数时,那就使用预规定的对齐系数。
- 假设一个对齐空间的大小等于对齐系数的大小。由于结构体中的数据都是按顺序存放的,如果上一个变量并没有使用完对齐空间且当前变量大小不大于上一个变量的剩余对齐空间的大小,则继续使用上一个变量剩余的对齐空间,否则开辟新的内存空间。
验证如下(实验所使用的编译器默认对齐系数为8):
4.1当成员变量的所有数据类型的大小都小于预规定的对齐系数时,编译器会自动选择占用字节数最大的数据类型的大小作为对齐系数.
#include<iostream>
using namespace std;
class Father{
public:
int a;
char b;
};
int main()
{
Father f;
cout<<"sizeof(f):"<<sizeof(f)<<endl;
return 0;
}
实验结果:
对齐规则:
#include<iostream>
using namespace std;
#pragma pack(4)
class Father{
public:
char b;
short d;
char c;
};
int main()
{
Father f;
cout<<"sizeof(f):"<<sizeof(f)<<endl;
return 0;
}
实验结果:
4.2如果有数据类型大小大于或等于预规定的对齐系数时,那就使用预规定的对齐系数。
#include<iostream>
using namespace std;
class Father{
public:
int a;
double c;
char b;
};
int main()
{
Father f;
cout<<"sizeof(f):"<<sizeof(f)<<endl;
return 0;
}
实验结果:
对齐规则:
#include<iostream>
using namespace std;
#pragma pack(4)//设置对齐系数为4
class Father{
public:
char c;
double a;
char b;
};
int main()
{
Father f;
cout<<"sizeof(f):"<<sizeof(f)<<endl;
return 0;
}
}
实验结果:
对齐规则:
4.3如果对齐空间有剩余
设一个对齐空间的大小等于对齐系数的大小。由于结构体中的数据都是按顺序存放的,如果上一个变量并没有使用完对齐空间且当前变量大小不大于上一个变量的剩余对齐空间的大小,则继续使用上一个变量剩余的对齐空间,否则开辟新的内存空间。
#include<iostream>
using namespace std;
class Father{
public:
int a;
char b;
double c;
};
int main()
{
Father f;
cout<<"sizeof(f):"<<sizeof(f)<<endl;
return 0;
}
实验结果:
对齐规则:
#include<iostream>
using namespace std;
class Father{
public:
double c;
int a;
char b;
};
int main()
{
Father f;
cout<<"sizeof(f):"<<sizeof(f)<<endl;
return 0;
}
实验结果:
对齐规则:
#include<iostream>
#include<stdio.h>
using namespace std;
#pragma pack(8)
class Father{
public:
char a;
char b;
int c;
char d;
char e;
};
int main()
{
Father f;
cout<<"sizeof(f):"<<sizeof(f)<<endl;
printf("%p\n",&(f.a));
printf("%p\n",&(f.b));
printf("%p\n",&(f.c));
printf("%p\n",&(f.d));
printf("%p\n",&(f.e));
return 0;
}
实验结果:
对齐规则:
#include<iostream>
#include<stdio.h>
using namespace std;
#pragma pack(8)
class Father{
public:
char a;
char b;
short f;
char g;
int c;
char d;
char e;
};
int main()
{
Father f;
cout<<"sizeof(f):"<<sizeof(f)<<endl;
printf("%p\n",&(f.a));
printf("%p\n",&(f.b));
printf("%p\n",&(f.f));
printf("%p\n",&(f.g));
printf("%p\n",&(f.c));
printf("%p\n",&(f.d));
printf("%p\n",&(f.e));
return 0;
}
实验结果:
对齐规则:
补充:如果对齐系数比类型的长度小,系统会依次读取该数据所占的内存,然后用某种算法,还原该数据。对于没有存放数据的对齐空间,系统读取后会丢弃(如上面的x)无用的数据。