内存对齐的规则以及作用

首先由一个程序引入话题:
  1. //环境:vc6 + windows sp2
  2. //程序1
  3. #include <iostream>

  4. using namespace std;

  5. struct st1 
  6. {
  7.     char a ;
  8.     int b ;
  9.     short c ;
  10. };
  11.  
  12. struct st2
  13. {
  14.     short c ;
  15.     char a ;
  16.     int b ;
  17. };

  18. int main()
  19. {
  20.     cout<<"sizeof(st1) is "<<sizeof(st1)<<endl;
  21.     cout<<"sizeof(st2) is "<<sizeof(st2)<<endl;
  22.     return 0 ;
  23. }
    程序的输出结果为:
    
    问题出来了,这两个一样的结构体,为什么sizeof的时候大小不一样呢?
    本文的主要目的就是解释明白这个问题。
    内存对齐,正是因为内存对齐的影响,导致结果不同。
    对于大多数的程序员来说,内存对齐基本上是透明的,这是编译器该干的活,编译器为程序中的每个数据单元安排在合适的位置上,从而导致了相同的变量,不同声明顺序的结构体的大小也不同。
    那么编译器为什么要进行内存对齐呢?程序1中结构体按常理来理解sizeof(str1)和sizeof(str2)结果都应该是7,4(int) + 2(short) + 1(char) = 7。经过内存对齐后,结构体的空间反而增大了。
    
     在解释内存对齐作用前,先来看下内存对齐的规则:
    (1)对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个暑假成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员自身长度)的倍数;
    (2)在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员大小中,比较小的那个进行;
    #pragma pack(n)表示设置为n字节对齐。VC 6.0默认8字节对齐。
    以程序1为例解释对齐的规则
    Str1:char占一个字节,起始偏移为0,int占4个字节,min(#pragma pack(),这个数据成员的自身长度) = 4,所以int按4字节对齐,起始偏移必须为4的倍数,所以起始偏移为4,在char后编译器会添加3个字节的额外字节,不存放任意数据。short占2个字节,按2个字节对齐,起始偏移为8,正好是2的倍数,无须添加额外字节。到此规则1的数据成员对齐结束,此时的内存状态为:
    
    共占用10个字节。还要继续进行结构体本身的对齐,对齐将按照#pragma pack()指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行,str1结构中最大数据成员长度为int,占4个字节,而默认的#pragma pack指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,需要添加2个额外字节使结构的总大小为12。此时的内存状态为:
    
    到此内存对齐结束。Str1占用了12个字节而非7个字节。
    Str2的对齐方法和Str1相同,读者可自己完成。

     内存对齐的主要作用是:
    (1)平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取得某些特定类型的数据,否则抛出硬件异常。
    (2)性能原因:经过内存对齐后,CPU的内存访速度大大提升。具体原因稍后解释。
     图一:
    
    这是普通程序员心目中的内存印象,由一个个的字节组成,而CPU并不是这么看待的。
     图二:
    
    CPU把内存当成是一块一块的,块的大小可以是2,、4、8、16字节的大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory granularity(粒度),本人把它翻译为“内存读取粒度”。
    假设CPU要读取一个int型4字节大小的数据到寄存器中,分为两种情况讨论:
    (1)数据从0字节开始
    (2)数据从1字节开始
    再次假设内存读取粒度为4。
     图三:
     内存对齐的规则以及作用_第1张图片
    当该数据是从0字节开始时,CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。
    当该数据是从1字节开始时,问题变得有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据。
     图四:
     内存对齐的规则以及作用_第2张图片
    此时,CPU先访问一次内存,读取0-3字节的数据进寄存器,并再次读取4-5字节的数据进寄存器,接着把0字节和6、7、8字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU的性能。
    
    图片来自: http://www.ibm.com/developerworks/library/pa-dalign/
    如大家对内存对齐对性能的具体影响感兴趣,可以参考上文。

    原文引自: http://www.cppblog.com/snailcong/archive/2009/03/16/76705.html

你可能感兴趣的:(c/c++)