C++内存对齐原则

C++内存对齐遵循数据成员对齐规则,确保结构体成员存储在大小的整数倍位置。结构体作为成员时,从最宽基本类型成员的整数倍开始存储。此外,结构体总大小需为最大成员的整数倍。内存对齐的目的是为了平台移植性和性能优化,避免额外的内存访问操作降低CPU效率。
C++内存对齐原则
(1)在没有#pragam pack宏的情况下,struct/class/union内存对齐原则有四个:
  1.      数据成员对齐规则:结构(struct)或联合(union)的数据成员,第一个数据成员放在offset为0的位置,以后每个数据成员存储的起始位置都是放在该数据成员大小的整数倍位置。如在32bit的机器上,int的大小为4,因此int的存储位置都是从4的整数倍的位置开始存储的。
  2. 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部“最宽基本类型成员”的整数倍地址开始存储(struct a里面有struct b,b里面有char、int、double等元素,那b应该从8的整数倍位置开始存储)。
  3. 收尾工作:结果体的总大小,也就是sizeof的结果,必须是其内部最大成员的“最宽基本类型成员”的整数倍,不足时要补齐。(基本类型不包括struct、class、union)
  4. sizeof(union),以结构里面size最大元素为union的size,因为在某一个时刻,union只有一个成员真正存储于该地址。
举例说明一下:
class A
{
     char a;
     int b;
};
class B
{
     char c;
     double d;
};

cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;


上例输出结果为8和16,原因是A的最大数据元素为int,大小为4,补齐之后为1+(3)+4 = 8;B的最大数据元素为double,大小为8,补齐之后为1+(7)+8=16.
class C
{
     char a;
     int b;
     char c;
};
class D
{
     char a;
     char c;
     int b;
};
cout<<sizeof(C)<<endl;
cout<<sizeof(D)<<endl;


上例输出结果为12和8。原因是数据放入内存的时候,是按内存块存放的,所以C的大小为1+(3)+4+1+(3)=12,而D的大小为1+1+(2)+4 = 8.
class A
{
     char str[21];
};
class B
{
     A a;
     int b;
};
cout<<sizeof(B)<<endl;


上例中输出结果为28.原因是当一个一个的存入内存块时,当存到第5个内存块时,A哭里只剩下1个char,下一个时int,大小为4,放不下,需要补齐,因此21+(3)+4 = 28.


(2)内存对齐的主要作用

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

2、  性能原因:经过内存对齐后,CPU的内存访问速度大大提升。具体原因稍后解释。

 

图一:

这是普通程序员心目中的内存印象,由一个个的字节组成,而CPU并不是这么看待的。

 

图二:

CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度) 本人把它翻译为“内存读取粒度” 。

 

假设CPU要读取一个int型4字节大小的数据到寄存器中,分两种情况讨论:

1、数据从0字节开始

2、数据从1字节开始

 

再次假设内存读取粒度为4。

 

图三:

当该数据是从0字节开始时,很CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。

    当该数据是从1字节开始时,问题变的有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据。

 

图四:

 

此时CPU先访问一次内存,读取0—3字节的数据进寄存器,并再次读取4—5字节的数据进寄存器,接着把0字节和6,7,8字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU性能。


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

(3)在使用#pragam pack(n)的情况下:
#pragam pack(1)
class A
{
     char a;
     int b;
     double d;
};
cout<<sizeof(A)<<endl;

上例输出结果为13,即1+4+8=13.看懂#pragam pack(1)的作用了吗?告诉编译器按照1来进行内存对齐,即没有内存对齐!
很明显#pragma pack(n)作为一个预编译指令用来设置多少个字节对齐的。值得注意的是,n的缺省数值是按照编译器自身设置,一般为8,合法的数值分别是1、2、4、8、16。即编译器只会按照1、2、4、8、16的方式分割内存。若n为其他值,是无效的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值