内存对齐


         

       想必每位学习计算机的同学都应该知道,在C语言中有这么一个知识点--内存对齐,而面试题中关于这个知识点的题目也屡见不鲜,所以今天我就来浅显的谈一谈我对内存对齐的理解。


为什么要内存对齐

0.平台移植:各个硬件平台对对存储空间的处理有很大的不同,比如有些架构的CPU在访问一个没有进行对

                        齐的变量的时候会发生错误,这就需要我们保证内存对齐。

1.提高效率因为处理器读写数据时,并不是以字节为单位,而是以块(2,4,8,16字节)为单位进行的。如

                         果不进行对齐,那么本来只需要进行一次的访问,可能需要好几次才能完成。假设内存的读取

                         粒度是4,cpu要读取一个int型(4字节)大小的内存。如果数据是从0字节开始,那么cpu只需

                        一次将0,1,2,3四个单元的内容读取出来即可;而如果你的数据跨越了这个边界,假设数据  

                         是从2字节开始,那么这个时候CPU先访问一次内存,读取0-3字节进寄存器,再读取4-7字节进

                         寄存器,然后把0、6、7、8字节的数据删除掉,最后合并1-4字节的数据。可以看出,如果内存

                         没对齐,所进行的操作要复杂得多,所以效率当然会降低。


内存对齐的规则

      每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令
      #pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

1.数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以
                                         后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,    
                                         比较小的那个进行。

      
  解析:struct或union的数据成员,第一个数据放在offset为0的地方,以后每个成员的存储起始位置要从
                   该成员数据类型大小的整数倍开始


2.结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按
                                                          照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的
                                                          那个进行。
         
          解析:结构或联合体的总大小,也就是sizeof的结果,必须是其内部成员最大数据类型的整数倍,不
                      足的要补齐。


3.结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任
                                     何效果。


/*本文例子都是在32位环境下适用的,
微软是严格按照对齐规则来分配内存的,但是linux下的编译器如gcc等,那就不一定了,要注意,gcc对任何2字节大小的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)都以4为对齐模数。*/

struct data1
{
       double;     d;        //8字节
       int         i;        //4字节
       char        c1;       //1字节
       char        c2[9];    //9字节       

}

sizeof(struct data1)的值是24

 首先按照储存大小,double8字节,int4字节,根据规则184的倍数,满足规则,char
 1
个字节,81的倍数,满足规则,最后一个c2[9]9个字节。8+4+1+9=22,而根据规则2,该结
 
构体显然是以8字节对齐的,而22不是8的倍数,所以需要进行对齐,对齐后的值即为24


union data1
{ 
       double;     d;        //8字节
       int         i;        //4字节
       char        c1;       //1字节
       char        c2[9];    //9字节

};


sizeof(union data1)的值是16。

这个程序与上一个程序的变量完全一样,只是一个是结构体,一个是共用体,可sizeof的结果却相差甚远,这是因为,共用体是共用一块内存,而sizeof的结果则是共用体中占内存最大的变量的所占字节数。该共用体最大基本类型为double,它占8字节,所以此共用体以8来对齐。字符数组c2占9个字节,那么整个共用体应该占9个字节,但按照规则,实际应该分配给它的内存为8的倍数即16字节。

struct data2
{
       char      c1;        //1字节
       char      c2[3];     //3字节

};

sizeof(struct data2)的值为4。

该结构体占内存空间最大的基本数据类型为char,长度为1,所以该结构体按1来对齐,c2占3字节,是1的倍数,满足规则,而1+3=4也是1的倍数,所以该结构体的分配的内存空间为4字节。



union data2
{
       char     c1;       //1字节
       char     c2[3];    //3字节

};


sizeof(union data2)的值为3。

同上面的struct data2类似,以1来对齐,分配的存储空间为3。


struct data3
{
        char       c1;    //1字节 
        double     d;     //8字节
        char       c2;    //1字节

};



sizeof(struct data3)的值为24。

这个结构体显然是以8字节对齐的。在分配空间时,首先c1被分配了1个字节,而接下来的d为8字节,根据规则1,它存储的起始位置要从8的整数倍开始,即要从8开始分配,所以前面c1的0字节后,要被补从1-7上7个字节,最后给c3分配了一个字节,8+8+1=17,但根据规则2需要进行对齐,对齐为8的倍数,所以最后的结果是24。



struct data4
{
        char    c1;    //1字节
        char    c2;    //1字节
        double  d;     //8字节

};


sizeof(struct data4)的值为16。

这个结构体也是以8字节对齐的,但编译器编译程序时,给c1,c2分配存储空间没必要,各自给他们分配8字节,两个一共8字节就可以了,给d自然也分配8字节,因此sizeof的值为16。也可以这么理解,c1占1个字节,1是8的整数倍,满足规律,c2占1个字节,1是8的倍数,满足规律,d占8个字节,根据规律1,它存储的起始位置要从8的整数倍开始,即要从8开始分配,所以前面只要补6个字节就可以了。所以一共分配16个字节。

补充一点:如果一个结构体里有含有某些结构体,则该结构体的对齐系数其内部最大的基本数据类型(包括被包含的结构体中的数据类型)。

struct inner
{
        char       c1;    //1字节 
        double     d;     //8字节
        char       c2;    //1字节

};
struct data4
{
        struct inner   t1; //24字节
        int            i;  //4字节
        char           c;  //1字节
};

sizeof(struct data4)的值为32。

struct data4是以8个字节对齐的,因为它的成员中最长的数据类型是double 8字节,t1占24个字节,i占4个字节,是8的倍数,满足规则1,c占1个字节,24+4+1=29不是8的倍数,所以内存补齐为32个字节。



struct inner
{
        char       c1;    //1字节 
        double     d;     //8字节
        char       c2;    //1字节

};
union data3
{
        struct inner   t1; //24字节
        int            i;  //4字节
        char           c;  //1字节
} ;

sizeof(struct data3)的值为24。

同上一个例子一样,union data3是以8字节对齐的,它的存储长度取决与t1,因为它所占的内存空间最大,而t1长度为24,是8的倍数,因此sizeof(union data3)的值为24。

struct data
{
       int     a;     //4字节
       long    b;     //4字节
       double  c;     //8字节
       float   d;     //4字节
       char    e[3];  //3字节
       short   f;     //2字节
 
}DATA;

这个结构体所占的内存是多少呢?首先。这个结构体是以8字节对齐的,在分配存储空间时,a分配4个字节,b分配4个字节,c分配8个字节,d分配4个字节,e分配3个字节,f占2个字节,可前面a,b,c占了16个字节,d,e占了7个字节,而根据规则1,f要从2的整数倍开始存储,所以给前面的e补齐为4个字节,这样,该结构体此时共占4+4+8+4+4+2=26个字节,再根据规则2,将该结构体的内存补齐为8的倍数,即变成32个字节。






















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值