内存对齐(只针对于自定义数据类型,基本数据类型无内存对齐概念)
为什么要内存对齐?
① 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
② 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
C语言和C++语言中空结构体所占的空间
在C++中规定了空结构体和空类的内存所占大小为1字节,因为c++中规定,任何不同的对象不能拥有相同的内存地址。而在C语言中,空的结构体在内存中所占大小为0。(gcc中测试为0,其他编译器不一定)
#pragma pack(4) // 定义内存对齐数为4个字节
#include <iostream>
using namespace std;
struct Person
{
int age;
int height;
int weight;
};
struct Student // 空结构体
{
};
int main()
{
Person PersonObject{ 17, 189, 65 }; // 聚合初始化
Student StudentObject;
cout << "Person类型结构体的字节数为" << sizeof(PersonObject) << endl; // 字节数为12
cout << "Student类型的结构体的字节数为" << sizeof(StudentObject) << endl; // 字节数为1
}
“留一个字节来区分不同类型的结构体存储位置”仅限于“空结构体”,有成员数据的结构体,自己已经有确切的存储位置无需在留有一个额外的存储空间来进行不同类型结构体的区分!
内存对齐的作用
内存对齐的作用不仅是便于cpu快速访问,同时合理的利用内存对齐可以有效地节省存储空间。
对于32位机来说,4内存对齐能够使cpu访问速度提高,比如说一个long类型的变量,如果跨越了4字节边界存储,那么cpu要读取两次,这样效率就低了。但是在32位机中使用1字节或者2内存对齐,反而会使变量访问速度降低。所以这要考虑处理器类型,另外还得考虑编译器的类型。在vc中默认是4内存对齐的,GNU -gcc 也是默认4内存对齐。
内存对齐的操作
伪指令#pragma pack (n) |
C编译器将按照n个字节对齐 |
伪指令#pragma pack () |
取消自定义字节对齐方式 |
内存对齐是什么?
内存对齐说白了就是“CPU一次性读取的字节数”。
内存对齐的规则
① 数据成员对齐的规则:第一个数据成员应该放在offset为0的地方,以后每个数组成员应该放在offset为min(当前数据成员的大小,#pargama pack(n))整数倍的地方开始(例如:int在32位机器中占用4个字节,#pargama pack(2),那么从”min(4,2)=2”的倍数的地方开始存储)。
② 结构体总的大小,也就是sizeof的结果,必须是min(结构体内部最大的成员,#pargama pack(n)) 的整数倍,不足要补齐(对齐每个元素后,还要对整个结构体再来一次总的对齐,这样是有利于CPU读取接下来的内存区域)
③ 结构体作为成员的对齐规则则于普通变量稍有不同,如果结构体B中,嵌套了结构体A,那么这个结构体A的起点为A内部最大成员的整数倍的地方(例如:结构体B嵌套结构体A,结构体A中包括double,int,short类型等成员,最大的数据类型为double占用8个字节,因此结构体A应该从8的整数倍开始存储)。
#pragma pack(4) // 定义内存对齐数为4个字节
#include <iostream>
#include <string>
using namespace std;
struct Person
{
int age; // min(4,4)=4,由于是开始的第一个元素,因此从offset=0处开始存储,占用第0~3字节的内存
short height; // min(2,4)=2,因此从(2字节数的整数倍)第4字节开始存储,占用第5~7字节的内存
char sex; // min(1,4)=1,因此从(1字节数的整数倍)第7个字节开始存储,占用第8字节的内存
double weight; // min(8,4)=4,因此从(4字节数的整数倍)第8个字节开始存储,占用第9~16字节的内存
// 最终16字节是min(8,4)=4字节的整数倍,因此不用在进行内存对齐
};
struct Student // 嵌套Person类型的结构体
{
short StudNum; // 由于是第一个变量,因此从offset=0的位置开始存储,占用第0~1字节的内存
Person PersonObject;
// Person类型结构体中最大元素占用8个字节的内存空间,min(8,4)=4,因此内存开始地址为第4字节,占用第5~20字节的内存
string name; // min(28,4)=4,因此内存开始地址为第20字节,占用字节21~48字节
// 最终48字节是min(16,4)=4字节的整数倍,因此不用在进行内存对齐
};
int main()
{
Person PersonObject{ 17, 189, 'f',65 }; // 聚合初始化
Student StudentObject;
cout << "Person类型结构体的字节数为" << sizeof(PersonObject) << endl; // 字节数为16
cout << "Student类型的结构体的字节数为" << sizeof(StudentObject) << endl; // 字节数为48
//cout << sizeof(string) << endl; // 单个string类型变量占用28个字节
}