日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

内存对齐、内存对齐规则解释、内存对齐原理

發(fā)布時間:2023/12/20 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 内存对齐、内存对齐规则解释、内存对齐原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、內(nèi)存對齊的原因

我們都知道計算機是以字節(jié)(Byte)為單位劃分的,理論上來說CPU是可以訪問任一編號的字節(jié)數(shù)據(jù)的,我們又知道CPU的尋址其實是通過地址總線來訪問內(nèi)存的,CPU又分為32位和64位,在32位的CPU一次可以處理4個字節(jié)(Byte)的數(shù)據(jù),那么CPU實際尋址的步長就是4個字節(jié),也就是只對編號是4的倍數(shù)的內(nèi)存地址進行尋址。同理64位的CPU的尋址步長是8字節(jié),只對編號是8的倍數(shù)的內(nèi)存地址進行尋址,如下圖所示是64位CPU的尋址示意圖:

這樣做可以實現(xiàn)最快速的方式尋址且不會遺漏一個字節(jié),也不會重復尋址。

那么對于程序而言,一個變量的數(shù)據(jù)存儲范圍是在一個尋址步長范圍內(nèi)的話,這樣一次尋址就可以讀取到變量的值,如果是超出了步長范圍內(nèi)的數(shù)據(jù)存儲,就需要讀取兩次尋址再進行數(shù)據(jù)的拼接,效率明顯降低了。例如一個double類型的數(shù)據(jù)在內(nèi)存中占據(jù)8個字節(jié),如果地址是8,那么好辦,一次尋址就可以了,如果是20呢,那就需要進行兩次尋址了。這樣就產(chǎn)生了數(shù)據(jù)對齊的規(guī)則,也就是將數(shù)據(jù)盡量的存儲在一個步長內(nèi),避免跨步長的存儲,這就是內(nèi)存對齊。在32位編譯環(huán)境下默認4字節(jié)對齊,在64位編譯環(huán)境下默認8字節(jié)對齊。

查看自己電腦是多少位操作系統(tǒng)
終端下輸入:uname -a 回車
x86_64 表示系統(tǒng)為64位
i686 表示系統(tǒng)32位的

二、對齊規(guī)則
1:數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員的偏移為 #pragma pack 指定的數(shù)值和這個數(shù)據(jù)成員自身長度中較小那個的整數(shù)倍。
2:數(shù)據(jù)成員為結(jié)構(gòu)體:如果結(jié)構(gòu)體的數(shù)據(jù)成員還為結(jié)構(gòu)體,則該數(shù)據(jù)成員的“自身長度”為其內(nèi)部最大元素的大小。(struct a 里存有 struct b,b 里有char,int,double等元素,那 b “自身長度”為 8)
3:結(jié)構(gòu)體的整體對齊規(guī)則:在數(shù)據(jù)成員按照 #1 完成各自對齊之后,結(jié)構(gòu)體本身也要進行對齊。對齊會將結(jié)構(gòu)體的大小增加為 #pragma pack 指定的數(shù)值和結(jié)構(gòu)體最大數(shù)據(jù)成員長度中較小那個的整數(shù)倍。

(看不懂沒關(guān)系,因為我也沒看懂,也是打印之后才明白)

三、實例

typedef struct test1 {char a;//1int b;//4字節(jié)double c;//8char d[11];//11 }Test1;//數(shù)據(jù)成員int main(int argc, const char * argv[]) {Test1 t1;//xcode默認對齊系數(shù)8,即#pragma pack(8)//char a, 1<8按1對齊,offset=0 [0]//int b, 4<8按4對齊,char a占到[0],int b應(yīng)該從地址1開始排, 地址1不是對齊數(shù)4的倍數(shù),所以int b的首地址是從4的最小倍數(shù)開始,所以offset=4, [4...7];//double c, 8=8按8對齊,int b已經(jīng)占到[4...7],double c從地址8開始排,地址8是對齊數(shù)8的倍數(shù),所以double c的首地址是從8的最小倍數(shù)開始,offse=8 [8...15]//char d[11], 1<8按1對齊,double c已經(jīng)占到[8...15], char d[11]從地址16開始,地址16是對齊數(shù)1的倍數(shù),所以char d的,offset=16, 存儲位置[16...26]//最后為27位,又因為27不是內(nèi)部最大成員中double 8位字節(jié)的倍數(shù),所以補齊為32)printf(" %lu\n %p\n %p\n %p\n %p\n", sizeof(t1), &t1.a, &t1.b, &t1.c, &t1.d);return 0; }

結(jié)果:

320x7ffeefbff5680x7ffeefbff56c0x7ffeefbff5700x7ffeefbff578

以首地址0x7ffeefbff568為offset=0,
0x68=104,
0x6c=108,
0x70=112,
0x78=120,
0x7ffeefbff56c相對于0x7ffeefbff568便宜了4,
0x7ffeefbff570相對于0x7ffeefbff568便宜了8,
0x7ffeefbff578相對于0x7ffeefbff568偏移了16,符合規(guī)律
上面int b正好偏移4,double c正好偏移8,比較湊巧,如果char a[5]呢,使地址正好措開,此時int b應(yīng)該是[8…13],double c應(yīng)該是[16…23], char d[11]應(yīng)該是[24…34],34不是最大double c的整數(shù)倍,補齊到最小倍數(shù)40,所以結(jié)構(gòu)體的大小應(yīng)該是40.

400x7ffeefbff5600x7ffeefbff5680x7ffeefbff5700x7ffeefbff578 Program ended with exit code: 0

在結(jié)構(gòu)體struct Test1中添加一個結(jié)構(gòu)體Test2,看下結(jié)果:

typedef struct test2 {char a[13];//1 [0...13]double b;//8 [16...23]int c[11];//4 [24...67]float d;//4 [68...71] }Test2;typedef struct test1 {char a[5];//1 [0...4]int b;//4 [8...11]double c;//8 [16...23]char d[11];//11 [24...34]Test2 t2;// [40...111] Test2的'自身長度'為double b=8,所以從8的最小倍數(shù)開始,即40 }Test1;//數(shù)據(jù)成員 //此時: // char a[13];//1 [40...52] // double b;//8 [56...63] // int c[11];//4 [64...107] // float d;//4 [107...111]int main(int argc, const char * argv[]) {Test1 t1;Test2 t2;printf(" %lu\n", sizeof(t2));printf(" %lu\n", sizeof(t1));return 0; }

結(jié)果:

72112 Program ended with exit code: 0

把Test2中的double b注銷了,看下結(jié)果:

typedef struct test2 {char a[13];//1 [0...13]//double b;//8 [16...23]int c[11];//4 [16...59]float d;//4 [60...63] }Test2;typedef struct test1 {char a[5];//1 [0...4]int b;//4 [8...11]double c;//8 [16...23]char d[11];//11 [24...34]Test2 t2;//64 [36...99] 此時Test2的'自身長度'為int c=4,所以從4的最小倍數(shù)開始,即36開始,又因為100不是double c的倍數(shù),補齊到最小倍數(shù)104 }Test1;//數(shù)據(jù)成員int main(int argc, const char * argv[]) {Test1 t1;Test2 t2;printf(" %lu\n", sizeof(t2));printf(" %lu\n", sizeof(t1));return 0; } 64104 Program ended with exit code: 0

四、更改默認對齊系數(shù)

#pragma pack(2)//1、2、4、8、16,以2為例typedef struct test2 {char a[13];//1 [0...13]//double b;//8 [16...23]int c[11];//4 2<4 [14...57]從2的最小倍數(shù)開始,即14float d;//4 [58...61] }Test2; int main(int argc, const char * argv[]) {Test2 t2;printf(" %lu\n", sizeof(t2));return 0; }

結(jié)果:

62 Program ended with exit code: 0

參考:

https://www.jianshu.com/p/f01fe1ef892d

總結(jié)

以上是生活随笔為你收集整理的内存对齐、内存对齐规则解释、内存对齐原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。