当前位置: 代码迷 >> 综合 >> C++:内存对齐
  详细解决方案

C++:内存对齐

热度:45   发布时间:2024-03-09 11:44:15.0

1.什么是内存对齐

假设我们同时声明两个变量:

char a;

short b;

用&(取地址符号)观察变量a,

b的地址的话,我们会发现(以16位CPU为例):

如果a的地址是0x0000,那么b的地址将会是0x0002或者是0x0004。

那么就出现这样一个问题:0x0001这个地址没有被使用,那它干什么去了?答案就是它确实没被使用。因为CPU每次都是从以2字节(16位CPU)或是4字节(32位CPU)的整数倍的内存地址中读进数据的。如果变量b的地址是0x0001的话,那么CPU就需要先从0x0000中读取一个short,取它的高8位放入b的低8位,然后再从0x0002中读取下一个short,取它的低8位放入b的高8位中,这样的话,为了获得b的值,CPU需要进行了两次读操作。

但是如果b的地址为0x0002,

那么CPU只需一次读操作就可以获得b的值了。所以编译器为了优化代码,往往会根据变量的大小,将其指定到合适的位置,即称为内存对齐(对变量b做内存对齐,a、b之间的内存被浪费,a并未多占内存)。

2.为什么要内存对齐

当时学C++的时候就没仔细研究过内存对齐这个玩意儿,现在好好看看,终于明白了。

为什么要内存对齐?

1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常

2.硬件原因:经过内存对齐之后,CPU的内存访问速度大大提升。【这个应该是最主要的】

cpu把内存当成是一块一块的,块的大小可以是2,4,8,16 个字节,因此CPU在读取内存的时候是一块一块进行读取的,块的大小称为(memory granularity)内存读取粒度。

我们再来看看为什么内存不对齐会影响读取速度?

假设CPU要读取一个4字节大小的数据到寄存器中(假设内存读取粒度是4),分两种情况讨论:

1.数据从0字节开始

2.数据从1字节开始

解析:当数据从0字节开始的时候,直接将0-3四个字节完全读取到寄存器,结算完成了。

当数据从1字节开始的时候,问题很复杂,首先先将前4个字节读到寄存器,并再次读取4-7字节的数据进寄存器,接着把0字节,4,6,7字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器,对一个内存未对齐的寄存器进行了这么多额外操作,大大降低了CPU的性能。

但是这还属于乐观情况,上文提到内存对齐的作用之一是平台的移植原因,因为只有部分CPU肯干,其他部分CPU遇到未对齐边界就直接罢工了。

3.例子

 (1)

#include<iostream>
using namespace std;
struct A{char a;   // = * * *int b;    // = = = =short c;  // = = * *
};struct B{short c;  // = =char a;   // = *  这里PPB=4,c和a刚好放在一个内存块int b;    // = = = 
};
int main(){cout<<sizeof(A)<<endl;cout<<sizeof(B)<<endl;return 0;
}

以上结构体变量数量类型相同。但是sizeof却不同,

    sizeof(A) is 12

    sizeof(B) is  8

(2)

#pragma pack(2) //指定PPB为2struct T{
char a; //偏移地址0
char b; //偏移地址1
int c; //偏移地址2
};

则sizeof(T)=最后一个成员的偏移地址+最后一个成员数的长度=2+4=6。

这里不要看什么偏移,以这题为例,a占1字节,b占1字节,c占4字节

这里PPB为2,所以a和b刚刚好占用一个内存块(PPB=2字节)

即a和b之间不用字节填充,也同样只需要CPU读一次

 

  相关解决方案