位元組 記憶體 對齊

2021-08-08 19:04:43 字數 1678 閱讀 2727

這裡重點討論記憶體對齊問題和#pragma pack()的使用方法。

什麼是記憶體對齊?

先看下面的結構:

struct teststruct1

假設這個結構的成員在記憶體中是緊湊排列的,假設c1的位址是0,那麼s的位址就應該是1,c2的位址就是3,i位址就是4。也就是c1位址為

0000 0000,s位址為0000 0001,c2位址為0000 0003,i位址為0000 0004。

可是,我們在visual c++6.0 中寫乙個簡單的程式:

struct teststruct1 a;

printf("c1 %p,s %p,c2 %p,i %p\n",

(unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,

(unsigned int)(void*)&a.s - (unsigned int)(void*)&a,

(unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,

(unsigned int)(void*)&a.i - (unsigned int)(void*)&a);

執行,輸出:

c1 00000000, s 00000002, c2 00000004, i 00000008。

為什麼會這樣?這就是記憶體對齊二導致的問題。

字,雙字,和四字在自然邊界上不需要在記憶體中對齊。(對字,雙字,和四字來說,自然邊界分別是偶數字址,可以被4 整除的位址,和可以被8 整除的位址。)無論如何,為了

提高程式的效能,資料結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;然而,對齊的記憶體訪問僅需要一次訪

問。乙個字或雙字運算元跨越了4 位元組邊界,或者乙個四字運算元跨越了8 位元組邊界,被認為是未對齊的,從而需要兩次匯流排週期來訪問記憶體。乙個字起始位址是奇數但卻沒有跨

越字邊界被認為是對齊的,能夠在乙個匯流排週期中被訪問。某些操作雙四字的指令需要記憶體運算元在自然邊界上對齊。如果運算元沒有對齊,這些指令將會產生乙個通用保護異常。

雙四字的自然邊界是能夠被16 整除的位址。其他的操作雙四字的指令允許未對齊的訪問(不會產生通用保護異常),然而,需要額外的記憶體匯流排週期來訪問記憶體中未對齊的資料。預設情況下,編譯器預設將結構、棧中的成員資料進行記憶體對齊。因此,上面的程式輸

出就變成了:c1 00000000, s 00000002, c2 00000004, i 00000008。編譯器將未對齊的成員向後移,將每乙個都成員對齊到自然邊界上,從而也導致了整個結構的尺寸變大。儘管會犧牲

一點空間(成員之間有部分記憶體空閒),但提高了效能。也正是這個原因,我們不可以斷言sizeof(teststruct1)的結果為8。在這個例子中,sizeof(teststruct1)的結果為12。

以上是來自《c語言深度剖析》的言論

下面是個人對這個結構體struct teststruct1 a大小的理解:

第乙個變數c1時char型別,佔乙個位元組。第二個變數s時short型別,佔兩個位元組。所以變數s要與變數c1對齊,c1就得佔兩個位元組。

c2本來占用1個位元組,由於變數i要與它對齊,c2就得佔4個位元組。至於變數i,它在結構體最後面,就佔本身記憶體4個位元組。

用下面**更清晰:

記憶體位元組對齊

一 什麼是對齊,以及為什麼要對齊 1.現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定變數的時候經常在特定的記憶體位址訪問,這就需要各型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這就是對齊。2.對齊的作...

記憶體位元組對齊

之前對記憶體位元組序知道一些,但是一直沒有系統的學習過,導致有時候還是說不清楚的,今天在網上查到一些自己,學習了下,並驗證了,特意給大家分享下,希望對大家能有幫助 總的來說就三條原則 在沒有 pragma pack巨集的時候 1 資料成員對齊規則,在結構體 struct 中,第乙個資料成員從0開始,...

記憶體位元組對齊

在c語言面試和考試中經常會遇到記憶體位元組對齊的問題。今天就來對位元組對齊的知識進行小結一下。首先說說為什麼要對齊。為了提高效率,計算機從記憶體中取資料是按照乙個固定長度的。以32位機為例,它每次取32個位,也就是4個位元組 每位元組8個位,計算機基礎知識,別說不知道 位元組對齊有什麼好處?以int...