内存对齐
文章目录
为什么需要内存对齐
尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度.
现在考虑4字节存取粒度的处理器取int类型变量(32位系统),该处理器只能从地址为4的倍数的内存开始读取数据。
假如没有内存对齐机制,数据可以任意存放,现在一个int变量存放在从地址1开始的联系四个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),然后从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器.这需要做很多工作.
也就是说内存对齐以后可以增加我们访问数据的效率,也就是为对我的内存处理器需要做两次操作而对齐的内存处理器只需要做一次操作,第二个就是,有的机器只能从某个数的整数倍进行寻址,如果不是内存对齐,那么不同机器的代码可能不能运行
结构体对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”
(也叫对齐模数)。gcc
中默认#pragma pack(4)
,可以通过预编译命令#pragma pack(n),n = 1,2,4,8,16
来改变这一系数。
有效对其值:是给定值#pragma pack(n)
和结构体中最长数据类型长度中较小的那个。有效对齐值也叫对齐单位。
- 第一个成员在与结构体变量偏移量为0的地址处。(即结构体的首地址处,即对齐到0处)
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
- 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
对齐数: 该结构体成员变量自身的大小与编译器默认的一个对齐数的较小值
结构体大小计算大小三部曲
struct S
{
double d;
char c;
int i;
};
第一步:找出每个成员变量的大小将其与编译器的默认对齐数相比较,取其较小值为该成员变量的对齐数
VS默认对齐数是8
第二步:根据每个成员对应的对齐数画出它们在内存中的相对位置
第三步:通过最大对齐数决定最终该结构体的大小
通过图我们可以知道,绿色部分(double d成员占用)+红色部分(char c成员占用)+紫色部分(int i成员占用)+红色与紫色之间的白色部分(浪费掉了)总共占用了16个字节的内存空间。
我们需要将它们总共占用的内存空间(16)与结构体成员的最大对齐数(8)相比较,结构体的总大小为最大对齐数的整数倍,此时16正好是8的整数倍,所以该结构体在VS编译器下的大小就16个字节。即创建一个该类型的结构体变量,内存需为其开辟16个字节的内存空间。
注意:大多数情况下,成员变量已经占用的总字节个数并不一定正好为其成员变量中的最大对齐数的整数倍,这时我们需要将其扩大为最大对齐数的整数倍。
变量类型占用内存大小
类型 | 占用字节 |
---|---|
char | 1 |
short int | 2 |
int | 4 |
long | 4 |
flaot | 4 |
double | 8 |
long long | 8 |
int 的范围大概是
-2e9~2e9
long long 范围
-9e18~9e18