系統禁止編譯器在乙個結構的起始位置跳過幾個位元組來滿足邊界對齊要求,因此所有結構的起始儲存位置必須是結構中邊界要求最嚴格的資料型別所要求的位置。如某個機器的整型長度為4個位元組且它的起始儲存位置能夠被4整除,那麼結構體
struct allgn ;
在記憶體中的儲存的起始位置必須是乙個能夠被4整除的位址。a的位址跟整個結構體的起始位址乙個值,結構體中的整型b必須跳過3個位元組跳到合適的邊界上才能儲存,儲存在b之後的是最後乙個字元變數c。如果宣告了相同型別的第二個結構體變數,它的起始儲存位置也必須滿足4這個邊界,所以在第乙個結構體的後面還要跳過3個位元組才能夠儲存第二個結構。因此每個結構體占用12位元組記憶體,但只是用其中的6個位元組。《c和指標》「10.3結構的儲存分配」章節(page 227)內容。
記憶體對齊概念暫針對c中的結構體。掌握記憶體對齊主要是用來避免記憶體自動對齊,調整結構體所佔的記憶體達到最小。
為了提高程式的效能,資料結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;然而,對齊記憶體訪問僅需要一次訪問。字,雙字和四字在自然邊界上不需要記憶體對齊(對它們來說,自然邊界分別是偶數字址,可以被4或8整除的位址)。乙個字或雙字運算元跨越了4位元組邊界,或者乙個四字運算元跨越了8位元組邊界,被認為是未對齊的。乙個字起始位址是奇數,但沒有跨越字邊界被認為是對齊的。[c語言深度剖析—阮正沖]
自然邊界
在系統(編譯器)預設下,在儲存變數時,當前變數vn
的位址減去緊湊儲存關係區域的第乙個變數v1
的位址的差值為變數vn
長度的整數倍。此時當前變數vn
的位址被稱為自然邊界[自己推測的乙個概念]。
如以下結構體:
struct _testtest;
在32位機上,若&test.ch的值為0x0000,則位址為0x0004為test.i的自然邊界。在自然邊界前的位址段必須能夠容納下正整數個當前變數。int占用乙個字,那麼i應該出現在自然邊界自上(能夠整除4的位址之上)。
記憶體對齊
以上的結構體在32位編譯器下,位址為0x0000的test.ch佔乙個位元組記憶體後,按理說&test.i的起始位址應為0x0001,結束位址應為0x0003。但由於編譯器的記憶體對齊會將test.i自動後移到它的自然邊界上儲存,即&test.i的值為0x0004(假設編譯器記憶體對齊預設值為4),結束位址為0x0007。編譯器預設的記憶體對齊就是將當前變數儲存到自然邊界之上。
在記憶體對齊下,對於以下結構體:
struct _testtest;
假若
&test.i = 0x0000
,則&test.ch = 0x0004
且test.ch
元素會占用
4個位元組,也就是說
test
結構體還是會占用
8個位元組大小。
x86 32位編譯器的預設記憶體對齊值一般為變數自身的長度值(將其儲存在自身的自然邊界上)。對於c來說,可以使用預處理命令#pragram pack()來修改編譯器的預設記憶體對齊值。
如使用#pragram pack(2)修改記憶體對齊值為2後,記憶體對齊=min(2,sizeof(int)),&test.i的起始位址為0x0002,結束位址為0x0005。
綜上,記憶體對齊(以下3層,層層遞進)
struct _test2test2;
[x86 32bit]根據記憶體對齊的特徵瞄一眼結構體給出結構體所佔記憶體大小值:1 + 3 + 4 + 4 =12位元組。
[x86 32bit] sizeof(test2)= 12位元組。
分析:ch1佔1 byte,ch2佔3 bytes(兩位元組的空閒位址,由i對齊造成),i佔4 bytes,li佔4 bytes。
struct _test3test3;
[x86 32bit]瞄記憶體值為:4 + 12 + 4 = 20 bytes
[x86 32bit] sizeof(test3)= 20 bytes
分析:結構體內的結構體元素記憶體對齊方式:以結構體元素內佔記憶體最大的元素的長度對齊。
所以:ch1 佔4 bytes(有3bytes為空閒,由t2對齊造成),t2佔12 bytes(對齊方式不變),i佔4 bytes。
#pragma pack(n),n=1, 2, 4,8,…
#pragma pack(2)
struct _test3test3;
#pragram pack()
[x86 32bit]瞄記憶體值為:2 + 12 + 4 = 18bytes。此時int型別的i就跨越了4位元組的乙個邊界,被認為是記憶體是未對齊的。
[x86 32bit]sizeof(test3) = 18 bytes。
分析:ch1 佔2 bytes(1bytes空閒,由#pragram pack(2)造成,它的值比t2的對齊長度4要小),t2佔12 bytes[因為struct _test2不在#pragma pack(2)與#pragma pack()之間,struct _test2 t2只是乙個記憶體拷貝,故而t2的佔記憶體大小不變],i佔4 bytes。
#pragma pack(8)
struct _test3test3;
#pragram pack()
[x86 32bit] 瞄記憶體值:4 + 12 + 4 = 20 bytes
[x86 32bit]sizeof(test3)= 20 bytes
分析:ch1 佔4 bytes(#pragma pack(8) > t2的記憶體對齊值4),t2佔12 bytes,i佔4 bytes。
[x86 32bit] int i = 1;
0x00
0x00
0x00
0x01
0xijkm, 0xijk(m+1) , xijk(m+2), 0xijk(m+3)
[x86 32bit] int i = 1;
0x00
0x00x00
0x01
0xijk(m+3) 0xijk(m+2) 0xijk(m+1) 0xijkm
大端小端成為了儲存資料的乙個特點。在必要的時刻就需要明確大端小端的儲存方式來明確變數的值(人為判斷時)。如union型別就是乙個例子。
union _utestutest;
union型別變數內的所有元素的記憶體位址共享起始位址。為union變數開闢其內佔記憶體最大的那個記憶體值。
ch1(ch2)int[0]
int[1]
int[2]
int[3]
如以上sizeof(utest) = 4 bytes。且&utest.ch1、&utest.ch2、&utest.i的值都一樣。
union變數的某段記憶體位址上定是多元素的位址。故而給union變數的某元素賦值時,可能其它的元素的值也被「潛」定了。
如utest.i = 1;
大端下
0x00
0x00
0x00
0x01
ch1(ch2) i
utest.i,utest.ch1,utest.ch2起始位址相同。大端時,utest.i的值在高位址位元組中。
utest.ch1 =utest.ch2 = 0x00;
小端下
0x01
0x00
0x00
0x00
ch1(ch2) i
utest.i,utest.ch1,utest.ch2起始位址相同。大端時,utest.i的值在低位址位元組中。
utest.ch1 = utest.ch2 = 0x01;
測試了一下debian linux 5.0是小端模式。
如果是測試當給utest.ch1或utest.ch2賦值時,i的值為多少。這種情況先要對i的其它位(如utest.i=0)初始化,不然輸出來是亂碼。
c note over。
C struct和union 記憶體位元組對齊問題
資料成員對齊規則 結構 struct 或聯合 union 的資料成員,第乙個資料成員放在offset為0的地方,以後每個資料成員儲存的起始位置要從該成員大小的整數倍開始 比如int在 位機為 位元組,則要從 的整數倍位址開始儲存。結構體作為成員 如果乙個結構裡有某些結構體成員,則結構體成員要從其內部...
C struct和union 記憶體位元組對齊問題
資料成員對齊規則 結構 struct 或聯合 union 的資料成員,第乙個資料成員放在offset為0的地方,以後每個資料成員儲存的起始位置要從該成員大小的整數倍開始 比如int在 位機為 位元組,則要從 的整數倍位址開始儲存。結構體作為成員 如果乙個結構裡有某些結構體成員,則結構體成員要從其內部...
C struct 中位元組對齊問題
c struct 中位元組對齊問題 vc中下面幾個結構體大小分別是多少呢 struct mystruct struct mystruct pragma pack push 儲存對齊狀態 pragma pack 16 設定為16位元組對齊 struct test pragma pack pop 恢復對...