C++内存对齐

C++内存对齐,第1张

C++内存对齐

目录

需要内存对齐的原因:

基本数据类型的内存对齐: 

抽象数据类型的内存对齐:

抽象数据类型考虑内存对齐的好处:

对齐系数的作用:

结构体的内存对齐:

内存对齐的好处:

关于结构体内成员定义的顺序应该遵循这样一个原则:

空结构体所占字节


需要内存对齐的原因:

32位系统cpu一次寻址4个字节,64位系统一次寻址8个字节

以64位系统位列:CPU一次寻址都是从地址为8的倍数的地址开始寻址,如果存储的数据都是内存对齐的,即每一个数据的首地址也都是存放在以8为倍数的地址中,那么CPU一次寻址就可以读取整个数据。

如果没有内存对齐,数据都是一个接着一个存储的。那么为了访问未对齐的数据的内存,CPU处理器一次读取的内存数据(8字节)可能包含两个数据的存储数据,而导致数据读取不完整,需要作两次内存访问;而对齐的内存访问仅需要一次访问。

基本数据类型的内存对齐: 

计算机系统对基本类型数据在内存中放的位置做了限制,它们会要求这些数据的首地址是一个数(一般为4---32位系统或者8---64位系统)的整数倍,也就是说这些数据的首地址被放到4/8的整数倍的地址中。

抽象数据类型的内存对齐: 抽象数据类型考虑内存对齐的好处:

比如:结构体,结构体成员在结构体中的不同位置会导致整个结构体所占用的内存不同,如果知道了内存对齐对结构体内存的影响,就可以排布结构体成员在结构体中的位置,使结构体占据的内存最小

几个概念:

对齐系数:每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。

对齐系数的作用:

即系统要求每一个数据最小占用多少内存

32位系统,gcc中默认#pragma pack(4),64位系统默认:#pragma pack(8)

我本人Linux系统对齐系数是8;

struct person
{
        char c;
        double d;
};
int main()
{
        int len;
        len=sizeof(person);
        if(len==12)
                printf("Linux对齐系数为:4n");
        if(len==16)
                printf("Linux对齐系数为:8n");
        else printf("errorn");

        return 0;
}
输出:Linux对齐系数为:8

可以通过预编译命令#pragma pack(n),n = 1,2,4,8,16(2的次方)来改变这一系数。

当n=1时就是没有内存对齐的时候,所有数据都是挨着存储的

查看当前系统的对齐模数的命令:

#pragma pack (show)

有效对齐值:是给定值#pragma pack(n)和结构体中最长数据类型长度中较小的那个。有效对齐值也叫对齐单位。

结构体的内存对齐:

原则:

第一条规则:各成员的内存空间的首地址必须是对齐系数和变量本身大小较小者的整数倍,(即必须是有效对齐值的整数倍)

第二条规则:对于结构体来说,在其各个数据都对齐之后,结构体本身也需要对齐,即结构体占用的总大小应该为“对齐系数”和“最大数据长度”中较小值的整数倍。

eg:

struct data_test
{
 char  a;  
 short b;  
 char c[2];
 double d;  
 char e;    
 int f;    
 char g;    
}data;

根据第一条规则:各成员的内存空间的首地址必须是对齐系数和变量本身大小较小者的整数倍,这里对齐系数是 4,因此变量 a 、数组 c 、变量 e 、变量 g 的首地址需要满足 1 的倍数,变量 b 的首地址需要满足 2 的倍数,变量 d 的首地址需要满足 4 的倍数,变量 f 的首地址需要 4 的倍数。

那既然结构体内的成员都已经对齐了,为什么还存在第二条原则呢?也就是说为什么结构体内的成员已经内存对齐了,结构体本身还需要对齐?下面通过一个结构体数组来说明,比如我们定义了这样一个结构体数组:

struct data_test
{
 char  a;  
 short b;  
 char c[2];
 double d;  
 char e;    
 int f;    
 char g;    
}data[2];

我们在放置成员存储位置的时候,data[0] 按照成员对齐的原则依次存放,放到最后一个结构体成员时,如果不考虑结构体本身的对齐,按照数组元素是紧挨着存放的原则,那这个结构体数组应该是按照下图进行存储的:

从上图中我们可以看到虽然 data[0] 中的成员都对齐了,但是由于结构体本身的不对齐,导致 data[1] 中的好多成员都按照存储原则对齐,因此,在完成了结构体成员的内存对齐后,我们还需要依据第二条原则:结构体占用的总大小应该为“对齐系数”和“最大数据长度”中较小值的整数倍,来对结构体本身进行对齐,因此正确的结构体数组的存储位置应该是先存储的结构体对齐之后,后面的在接着存储,如下图所示: 

data[0] 最终的大小是 28 字节,结构体数组 data 的大小为 56 字节

注意:成员是数组时,计算有效对齐值时,是取其类型大小,而不是总的数组大小

当结构体嵌套结构体时:子结构体也需要先本身对齐,再存储下面的数据

总内存=子结构体内存+非结构体数据内存

内存对齐的好处:

以空间换时间

根据第一条原则,那么结构体成员定义的先后顺序会对最终结构体占用的内存造成影响,比如现在调整结构体 data 内成员的定义顺序,如下:

struct data_test
{
 char  a;  
 char c[2];
 char e;    
 char g;    
 short b;  
 int f;    
 double d;  
}data;

改变结构体成员顺序后的存储位置如下:

所以调整顺序后的结构体大小为 20 个字节,相比于之前没有改变顺序之前整整减少了 8 个字节

关于结构体内成员定义的顺序应该遵循这样一个原则:

按照长度递增的顺序依次定义各个成员 

空结构体所占字节

另外在C++中,一个空类和空结构体是占一个字节的空间的。在C语言中,空结构体不占空间。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/3970445.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-10-21
下一篇 2022-10-21

发表评论

登录后才能评论

评论列表(0条)

保存