Linux下C的記憶體對齊
本文全文參考:http://blog.csdn.net/wendy260310/article/details/17675983
該部落格是在Linux下C++測試的,我在他的基礎上測試了C語言的結構體,環境採用Windows下DEV,由於都是GCC編譯器,所以其本質是一樣的
我們先測試一下double,char,int在我的電腦上佔的位數:
the size of char :1
the size of int :4
the size of float :4
the size of double:8
一、預設情況下,
1.首先第一個問題就是為什麼要進行對齊,現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體地址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。
對齊的作用和原因:各個硬體平臺對儲存空間的處理上有很大的不同。一些平臺對某些特定型別的資料只能從某些特定地址開始存取。比如有些架構的CPU在訪問 一個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下程式設計必須保證位元組對齊.其他平臺可能沒有這種情況,但是最常見的是如果不按照適合其平臺要求對 資料存放進行對齊,會在存取效率上帶來損失。比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那 麼一個讀週期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit數
據。顯然在讀取效率上下降很多。
首先考慮下面的一個結構:
struct{
double da1;
char da2;
int da3;
}A;
那我們再看看結構體A佔用的位數:
printf("the size of struct A:%d",sizeof(A));
the size of struct A:16這說明編譯器給我們進行了記憶體對齊。
規則和vc上是一樣的:各成員變數存放的起始地址相對於結構的起始地址的偏移量必須為該變數的型別所佔用的位元組數的倍數.且結構的大小為結構的位元組邊界數(即該結構中佔用最大空間的型別所佔用的位元組數)的倍數。
char
偏移量必須為sizeof(char)即1的倍數.
int
偏移量必須為sizeof(int)即4的倍數.
float
偏移量必須為sizeof(float)即4的倍數.
double
偏移量必須為sizeof(double)即8的倍數.
最開始的時候,偏移為0,剛好是8的倍數,分配8B之後,偏移是8了,是1的倍數。到int的時候偏移是9了,不是4的倍數,所以需要進行位元組的對齊。填充3個位元組到12,所以最後的結果是16.16剛好是結構體佔用空間最大的型別(double)的倍數。
總結預設規則:
對於偏移量:變數type n起始地址相對於結構體起始地址的偏移量必須為sizeof(type(n))的倍數
結構體大小:必須為成員最大型別位元組的倍數
偏移量 | 大小 | 空地址 |
0 | 8 | |
8(是1的倍數) | 1 | +3 |
12(9不是4的倍數+3) | 4 |
(2)我把三個變數的位置換一下,看看結果:
struct{
char da1;
double da2;
int da3;
}B;
the size of struct B:24
現在變成24了,按照剛才所說的規則,分配完int之後結果是20,但是20不是最大型別(double)的倍數,所以要進行填充,到24.
從網上摘抄的:
在預設對齊下我先給你說下三條準則吧, 【1】結構體變數的首地址能夠被其最寬基本型別成員的大小所整除 【2】結構體每個成員相對於結構體首地址的偏移量是成員大小的整數倍 【3】結構體的總大小為結構體最寬基本型別成員大小的整數倍 以上都是結構體中只有基本型別時的預設對齊方式,當有巢狀複合成員時, 【2】改為:複合成員相對於結構體首地址偏移量是複合成員最寬基本型別大小的整數倍
二、不是預設情況下,#pragma pack(n)
Linux下也提供了和vc一樣的遮蔽預設對齊方式的指令。#pragma pack(n)來設定變數以n位元組對齊方式。
1,n位元組對齊就是說變數存放的起始地址的偏移量有兩種情況:
第一、如果n大於等於該變數所佔用的位元組數,那麼偏移量必須滿足預設的對齊方式,
第二、如果n小於該變數的型別所佔用的位元組數,那麼偏移量為n的倍數,不用滿足預設的對齊方式。
2,結構的總大小也有個約束條件,分下面兩種情況:
如果n大於等於所有成員變數型別所佔用的位元組數,那麼結構的總大小必須為佔用空間最大的變數佔用的空間數的倍數;否則必須是n的倍數。
下面是一個例子:
#pragma pack(4)
struct{
char da1;
double da2;
int da3;
}B;
測試這個Class的大小結果是16B.the size of struct B:16
最開始給da1分配空間,da1佔用的空間小於4,且最開始的偏移是0,滿足預設的對齊的方式,給它分配了一個位元組,到da2的時候,da2佔用的空間是大於4的,所以這個時候的偏移只需要是4的倍數,不像預設的方式必須是8的倍數。填上3個位元組,此時的偏移是4了,分配了8個位元組。到da3了,da3佔用的空間等於4,那需要滿足預設的對齊方式,現在的偏移是12了,滿足就直接給da3分配空間。大小為16,在看結構體的問題,結構體最大的比4大,所以最後的大小是4的倍數就可以了。
偏移量 | 大小 | 空 |
> 0 | 1 | +3 |
<(是n的倍數) 4 | 8 | |
>= 12 | 4 | |
16 |
我們把n改成8看看結果:
the
size of struct B:24
按照給定的規則分析就可以得到這個結果。