为什么要进行内存对齐以及对齐规则

#include<iostream>
using namespace std;
struct A{
    char a;
    int b;
    short c;
};

struct B{
    short c;
    char a;
    int b;
};
int main(){
    cout<<sizeof(A)<<endl;
    cout<<sizeof(B)<<endl;
    return 0;
}

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

    sizeof(A) is 12

    sizeof(B) is  8

那么问题来了,为什么两个一样的结构体,但是sizeof大小却不同?

   答案就是内存对齐导致结果不同

对于大多数程序员来说,内存对齐是透明的,这是编译器该干的活,编译器为程序中的每个数据单元安排在合适的位置上,从而导致了不同的声明顺序的结构体大小不同。

那么编译器为什么要进行内存对齐呢?本来sizeof(int +char +short)应该是7,对齐之后反而变大了,为什么?

先来看看内存对齐的规则?

1.对于结构的各个成员,第一个成员位于偏移为0的位置,以后的每个数据成员的偏移量必须是  min(#pragma pack()指定的数,这个数据成员的自身长度)的倍数

2.在所有的数据成员完成各自对齐之后,结构或联合体本身也要进行对齐,对齐将按照 #pragram pack 指定的数值和结构或者联合体最大数据成员长度中比较小的那个  也就是  min(#pragram pack() , 长度最长的数据成员);

   #pragram pack(n)  表示的是设置n字节对齐,vc6默认的是8

 

接下来以第一个结构体为例说明上述规则?

A:char占一个字节,起始偏移为零,int 占四个字节,min(8,4)=4;所以应该偏移量为4,所以应该在char后面加上三个字节,不存放任何东西,short 占两个字节,min(8,2)=2;所以偏移量是2的倍数,而short偏移量是8,是2的倍数,所以无需添加任何字节,所以第一个规则对齐之后内存状态为  0xxx|0000|00    

  此时一共占了10个字节,但是还有结构体本身的对齐, min(8,4)=4;所以总体应该是4的倍数,所以还需要添加两个字节在最后面,所以内存存储状态变为了 0xxx|0000|00xx   一共占据了12个字节

 

接下来我们好好讨论一下内存对齐的作用?

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

  2.硬件原因:经过内存对齐之后,CPU的内存访问速度大大提升。具体原因接下来解释

图一:

我们普通程序员心中的内存印象,由一个个字节组成,但是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遇到未对齐边界就直接罢工了。

参考图片:

posted @ 2015-10-04 15:15  jiguojing  阅读(23452)  评论(2编辑  收藏  举报