内存对齐、内存对齐规则解释、内存对齐原理
一、內存對齊的原因
我們都知道計算機是以字節(Byte)為單位劃分的,理論上來說CPU是可以訪問任一編號的字節數據的,我們又知道CPU的尋址其實是通過地址總線來訪問內存的,CPU又分為32位和64位,在32位的CPU一次可以處理4個字節(Byte)的數據,那么CPU實際尋址的步長就是4個字節,也就是只對編號是4的倍數的內存地址進行尋址。同理64位的CPU的尋址步長是8字節,只對編號是8的倍數的內存地址進行尋址,如下圖所示是64位CPU的尋址示意圖:
這樣做可以實現最快速的方式尋址且不會遺漏一個字節,也不會重復尋址。
那么對于程序而言,一個變量的數據存儲范圍是在一個尋址步長范圍內的話,這樣一次尋址就可以讀取到變量的值,如果是超出了步長范圍內的數據存儲,就需要讀取兩次尋址再進行數據的拼接,效率明顯降低了。例如一個double類型的數據在內存中占據8個字節,如果地址是8,那么好辦,一次尋址就可以了,如果是20呢,那就需要進行兩次尋址了。這樣就產生了數據對齊的規則,也就是將數據盡量的存儲在一個步長內,避免跨步長的存儲,這就是內存對齊。在32位編譯環境下默認4字節對齊,在64位編譯環境下默認8字節對齊。
查看自己電腦是多少位操作系統
終端下輸入:uname -a 回車
x86_64 表示系統為64位
i686 表示系統32位的
二、對齊規則
1:數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset為0的地方,以后每個數據成員的偏移為 #pragma pack 指定的數值和這個數據成員自身長度中較小那個的整數倍。
2:數據成員為結構體:如果結構體的數據成員還為結構體,則該數據成員的“自身長度”為其內部最大元素的大小。(struct a 里存有 struct b,b 里有char,int,double等元素,那 b “自身長度”為 8)
3:結構體的整體對齊規則:在數據成員按照 #1 完成各自對齊之后,結構體本身也要進行對齊。對齊會將結構體的大小增加為 #pragma pack 指定的數值和結構體最大數據成員長度中較小那個的整數倍。
(看不懂沒關系,因為我也沒看懂,也是打印之后才明白)
三、實例
typedef struct test1 {char a;//1int b;//4字節double c;//8char d[11];//11 }Test1;//數據成員int main(int argc, const char * argv[]) {Test1 t1;//xcode默認對齊系數8,即#pragma pack(8)//char a, 1<8按1對齊,offset=0 [0]//int b, 4<8按4對齊,char a占到[0],int b應該從地址1開始排, 地址1不是對齊數4的倍數,所以int b的首地址是從4的最小倍數開始,所以offset=4, [4...7];//double c, 8=8按8對齊,int b已經占到[4...7],double c從地址8開始排,地址8是對齊數8的倍數,所以double c的首地址是從8的最小倍數開始,offse=8 [8...15]//char d[11], 1<8按1對齊,double c已經占到[8...15], char d[11]從地址16開始,地址16是對齊數1的倍數,所以char d的,offset=16, 存儲位置[16...26]//最后為27位,又因為27不是內部最大成員中double 8位字節的倍數,所以補齊為32)printf(" %lu\n %p\n %p\n %p\n %p\n", sizeof(t1), &t1.a, &t1.b, &t1.c, &t1.d);return 0; }結果:
320x7ffeefbff5680x7ffeefbff56c0x7ffeefbff5700x7ffeefbff578以首地址0x7ffeefbff568為offset=0,
0x68=104,
0x6c=108,
0x70=112,
0x78=120,
0x7ffeefbff56c相對于0x7ffeefbff568便宜了4,
0x7ffeefbff570相對于0x7ffeefbff568便宜了8,
0x7ffeefbff578相對于0x7ffeefbff568偏移了16,符合規律
上面int b正好偏移4,double c正好偏移8,比較湊巧,如果char a[5]呢,使地址正好措開,此時int b應該是[8…13],double c應該是[16…23], char d[11]應該是[24…34],34不是最大double c的整數倍,補齊到最小倍數40,所以結構體的大小應該是40.
在結構體struct Test1中添加一個結構體Test2,看下結果:
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的最小倍數開始,即40 }Test1;//數據成員 //此時: // 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; }結果:
72112 Program ended with exit code: 0把Test2中的double b注銷了,看下結果:
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的最小倍數開始,即36開始,又因為100不是double c的倍數,補齊到最小倍數104 }Test1;//數據成員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四、更改默認對齊系數
#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的最小倍數開始,即14float d;//4 [58...61] }Test2; int main(int argc, const char * argv[]) {Test2 t2;printf(" %lu\n", sizeof(t2));return 0; }結果:
62 Program ended with exit code: 0參考:
https://www.jianshu.com/p/f01fe1ef892d
總結
以上是生活随笔為你收集整理的内存对齐、内存对齐规则解释、内存对齐原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python】模块学习之ConfigP
- 下一篇: iOS10 打开APP设置界面和WIFI