关于内存对齐

一.内部存款和储蓄器对齐的起来说解

内部存款和储蓄器对齐能够用一句话来总结:

“数据项只可以存款和储蓄在地方是数量项大小的整数倍的内部存款和储蓄器地点上”

譬如int类型占用4个字节,地址只可以在0,4,8等任务上。

例1:

#include <stdio.h>
struct xx{
        char b;
        int a;
        int c;
        char d;
};

int main()
{
        struct xx bb;
        printf("&a = %p/n", &bb.a);
        printf("&b = %p/n", &bb.b);
        printf("&c = %p/n", &bb.c);
        printf("&d = %p/n", &bb.d);
        printf("sizeof(xx) = %d/n", sizeof(struct xx));

        return 0;
}

实施结果如下:

&a = ffbff5ec
&b = ffbff5e8
&c = ffbff5f0
&d = ffbff5f4
sizeof(xx) = 16

会发掘b与a之间空出了3个字节,相当于说在b之后的0xffbff5e9,0xffbff5ea,0xffbff5eb空了出去,a直接存款和储蓄在了0xffbff5ec, 因为a的高低是4,只可以存款和储蓄在4个整数倍的岗位上。打字与印刷xx的大大小小会发觉,是16,某一个人大概要问,b之后空出了3个字节,那也应该是13哟?其余的3个 呢?这几个今后阅读本文少禽分晓的更加尖锐一些,这里差相当的少说一下正是d前面包车型地铁3个字节,也会浪费掉,也正是说,那3个字节也被这么些结构体占用了.

能够归纳的改换结构体的结构,来减少内部存款和储蓄器的运用,举个例子可以将组织体定义为:
struct xx{
        char b; 
        char d;
        int a;          
        int c;                  
};

如此打字与印刷这一个结构体的大小就是12,省了比非常多上空,能够看看,在概念结构体的时候,必须要思虑要内部存款和储蓄器对齐的震慑,那样能使咱们的顺序占用更加小的内部存储器。

二.操作系统的暗中同意对齐周到

每种操作系统都有和好的暗许内部存款和储蓄器对齐周到,倘使是新本子的操作系统,暗许对齐周全通常都以8,因为操作系统定义的最大连串存款和储蓄单元正是8个字节,比方long long定要这样,不设有超过8个字节的品类(例如int是4,char是1,long在三十二人编写翻译时是4,陆十二位编译时是 8)。当操作系统的默许对齐周详与第三节所讲的内部存款和储蓄器对齐的驳斥发生冲突时,以操作系统的对齐全面为尺度。(举例操作系统暗许的对齐周密为 4 , 那么char类型的多寡就不得空中楼阁0 , 4 ,8 等4的翻番地址上; 借使操作系统的私下认可对齐周到是4,那么对与long long这些类型的变量就不满意第三节所说的,也等于说long long这种结构,可以积攒在被4整除的岗位上,也能够储存在被8整除的职位上。)

能够透过#pragma pack()语句修改操作系统的默许对齐周到,

例2:

#include <stdio.h>
#pragma pack(4)
struct xx{
        char b;
        long long a;
        int c;
        char d;
};
#pragma pack()

int main()
{
        struct xx bb;
        printf("&a = %p/n", &bb.a);
        printf("&b = %p/n", &bb.b);
        printf("&c = %p/n", &bb.c);
        printf("&d = %p/n", &bb.d);
        printf("sizeof(xx) = %d/n", sizeof(struct xx));

        return 0;
}
打字与印刷结果为:

&a = ffbff5e4
&b = ffbff5e0
&c = ffbff5ec
&d = ffbff5f0
sizeof(xx) = 20

意识占用8个字节的a,存款和储蓄在了不能够被8整除的地方上,存款和储蓄在了被4整除的任务上,采用了操作系统的默许对齐周到。

三.内部存储器对齐产生的原委

 

内部存款和储蓄器对齐是操作系统为了火速访谈内部存款和储蓄器而利用的一种政策,简而言之,正是为了放置变量的贰次采访。操作系统在拜候内部存款和储蓄器时,每回读取一定的长短(那几个尺寸就是操作系统的暗许对齐周详,只怕是私下认可对齐周密的卡尺头倍)。若无内部存款和储蓄器对齐时,为了读取贰个变量是,会时有产生总线的二次访谈。

诸如假诺未有内部存款和储蓄器对齐,结构体xx的变量地方会现身如下景况:

struct xx{
        char b;         //0xffbff5e8
        int a;            //0xffbff5e9       
        int c;             //0xffbff5ed      
        char d;         //0xffbff5f1
};

操作系统先读取0xffbff5e8-0xffbff5ef的内部存款和储蓄器,然后在读取0xffbff5f0-0xffbff5f8的内存,为了赢得值c,就需求将两组内部存储器合併,进行整合,那样严重下落了内部存款和储蓄器的拜谒效用。(那就关乎到了老调重弹的主题材料,空间和功能哪个更主要?这里不做探讨)。

这么大家就能够明了为啥结构体的首先个变量,不管项目怎么着,都以能被8整除的啊(因为访谈内存是从8的整数倍开端的,为了充实读取的频率)!

 

内部存款和储蓄器对齐的标题重中之重存在于掌握struct等复合结构在内部存储器中的遍及。

第一要明了内部存款和储蓄器对齐的概念。
众多其实的Computer系统对基本类型数据在内部存款和储蓄器中存放的职位有限制,它们会需要这几个多少的首地址的值是某些数k(常常它为4或8)的倍数,那正是所谓的内存对齐。

其一k在分裂的cpu平台下,分化的编译器下展现也天差地远。比方叁十六位字长的管理器与十四位字长的Computer。大家的支付首要涉及两大平台,windows和linux(unix),涉及的编写翻译器也重视是microsoft编写翻译器(如cl),和gcc。

内部存储器对齐的指标是使各样基本数据类型的首地址为对应k的倍数,那是精晓内部存款和储蓄器对齐格局的极限法宝。别的还要区分编译器的独家。明白了这两点基本上就能够化解全体内部存款和储蓄器对齐方面包车型地铁标题。

分裂编写翻译器中的k:
1、对于microsoft的编写翻译器,每一个基本项指标尺寸即为那几个k。大意上char类型为8,int为32,long为32,double为64。
2、对于linux下的gcc编写翻译器,对于过量等于4字节的成员的发轫 地方应该处于4字节的莫西干发型倍上,对于2字节成员的苗子地点应该处于2字节的整数倍上,1字节的开场地方任意。

略知一二了上述的注明对struct等复合结构的内部存储器布满就应当很了然了。

上面看一下最轻易易行的一个种类:struct中成员都为核心数据类型,例如:
struct test1
{
char a;
short b;
int c;
long d;
double e;
};

在windows平台,microsoft编写翻译器下:

假若从0地点开头,首先a的k值为1,它的首地址能够使放肆地方,所以a占用第二个字节,即地址0;然后b的k值为2,他的首地址必须是2的翻番,不能够是1,所以地点1万分字节被填充,b首地址为地址2,占用地址2,3;然后到c,c的k值为4,他的首地址为4的翻番,所以首地方为4,占用地址4,5,6,7;再然后到d,d的k值也为4,所以他的首地址为8,占用地址8,9,10,11。最后到e,他的k值为8,首地方为8的翻番,所以地点12,13,14,15被填充,他的首地址应该为16,占用地址16-23。分明其大小为24。

在linux平台,gcc编写翻译器下:
一旦从0地点发轫,首先a的k值为1,它的首地址能够使大肆地点,所以a占用第一个字节,即地址0;然后b的k值为2,他的首地址必须是2的翻番,不可能是1,所以地点1不胜字节被填充,b首地址为地址2,占用地址2,3;然后到c,c的k值为4,他的首地址为4的翻番,所以首地址为4,占用地址4,5,6,7;再然后到d,d的k值也为4,所以他的首地址为8,占用地址8,9,10,11。最终到e,从那边开端与microsoft的编写翻译器初始享不相同,他的k值为不是8,还是是4,所以其首地址是12,占用地址12-19。明显其大小为20。

接下去,看一看几类特殊的处境,为了制止麻烦,不再描述内部存款和储蓄器分布,只总括结构大小。

首先种:嵌套的构造

struct test2
{
char f;
struct test1 g;
};

在windows平台,microsoft编写翻译器下:

这种情状下要是把test2的第三个分子拆开来,切磋内部存款和储蓄器布满,那么能够理解,test2的分子f占用地址0,g.a占用地址1,将来的内部存款和储蓄器布满不改变,依旧满意全部骨干数据成员的首地址都为其对应k的倍数这一准绳,那么test2的深浅就依然24了。可是事实上test2的高低为32,那是因为:不可能因为test2的组织而退换test1的内部存款和储蓄器布满景况,所感到了使test1种各类成员依旧满足对齐的须求,f成员后边需求填写一定数额的字节,(恐怕是windows暗许的对齐周全为8 , 所以struct需求从8的倍数起先储存,因为对此结构体,它的分寸应该是编写翻译器最大对齐模数的偏分头倍))轻松察觉,这些数额应该为7个,技艺确认保证test1的对齐。所以test2相对于test1来讲扩大了8个字节,所以test2的轻重缓急为32。

在linux平台,gcc编写翻译器下:

未有差距于,这种境况下一旦把test2的第三个成员拆开来,研究内部存款和储蓄器布满,那么能够掌握,test2的分子f占用地址0,g.a占用地址1,今后的内部存储器分布不改变,如故满意全体大旨数据成员的首地址都为其对应k的倍数这一口径,那么test2的轻重就好像故20了。不过其实test2的大小为24,一样那是因为:不能够因为test2的布局而改造test1的内部存款和储蓄器布满景况,所以为了使test1种各种成员依旧满意对齐的渴求,f成员后边要求填写一定数额的字节(估算是因为Linux的私下认可对齐周全为4 , 所以struct成员须要从4 的倍数开头积攒,因为对此结构体,它的尺寸应该是编写翻译器最大对齐模数的大背头倍),轻巧察觉,这几个数量应该为3个,本事担保test1的对齐。所以test2相对于test1来讲增添了4个字节,所以test2的分寸为24。

 

 

有关嵌套的struct的存款和储蓄仍旧不明白, 待未来退换。

初稿地址 :  

本文由银河网址发布于银河网址,转载请注明出处:关于内存对齐

您可能还会对下面的文章感兴趣: