pragma pack n 也談記憶體對齊

2021-04-18 08:17:45 字數 4243 閱讀 7937

在最近的專案中,我們涉及到了

「記憶體對齊

」技術。對於大部分程式設計師來說,

「記憶體對齊

」對他們來說都應該是

「透明的」。

「記憶體對齊

」應該是編譯器的

「管轄範圍

」。編譯器為程式中的每個

「資料單元

」安排在適當的位置上。但是

c語言的乙個特點就是太靈活,太強大,它允許你干預

「記憶體對齊」「

記憶體對齊

」對你就不應該再透明了。

一、記憶體對齊的原因

大部分的參考資料都是如是說的:

1、平台原因

(移植原因

):不是所有的硬體平台都能訪問任意位址上的任意資料的;某些硬體平台只能在某些位址處取某些特定型別的資料,否則丟擲硬體異常。

2、效能原因:資料結構

(尤其是棧

)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問。

二、對齊規則

每個特定平台上的編譯器都有自己的預設

「對齊係數

」(也叫對齊模數

)。程式設計師可以通過預編譯命令

#pragma pack(n)

,n=1,2,4,8,16

來改變這一係數,其中的

n就是你要指定的

「對齊係數」。

規則:1

、資料成員對齊規則:結構

(struct)(

或聯合(union))

的資料成員,第乙個資料成員放在

offset為0

的地方,以後每個資料成員的對齊按照

#pragma pack

指定的數值和這個資料成員自身長度中,比較小的那個進行。

2、結構

(或聯合

)的整體對齊規則:在資料成員完成各自對齊之後,結構

(或聯合

)本身也要進行對齊,對齊將按照

#pragma pack

指定的數值和結構

(或聯合

)最大資料成員長度中,比較小的那個進行。

3、結合1、

2顆推斷:當

#pragma pack的n

值等於或超過所有資料成員長度的時候,這個

n值的大小將不產生任何效果。

三、試驗

我們通過一系列例子的詳細說明來證明這個規則吧!

我試驗用的編譯器包括

gcc 3.4.2

和vc6.0的c

編譯器,平台為

windows xp + sp2

。我們將用典型的

struct

對齊來說明。首先我們定義乙個

struct

:#pragma pack(n) /* n = 1, 2, 4, 8, 16 */

struct test_t ;

#pragma pack(n)

首先我們首先確認在試驗平台上的各個型別的

size

,經驗證兩個編譯器的輸出均為:

sizeof(char) = 1

sizeof(short) = 2

sizeof(int) = 4

我們的試驗過程如下:通過

#pragma pack(n)改變「

對齊係數

」,然後察看

sizeof(struct test_t)

的值。1、1

位元組對齊

(#pragma pack(1))

輸出結果:

sizeof(struct test_t) = 8 [

兩個編譯器輸出一致]

分析過程:

1) 成員資料對齊

#pragma pack(1)

struct test_t ;

#pragma pack()

成員總大小

=8

2) 整體對齊

整體對齊係數

= min((max(int,short,char), 1) = 1

整體大小

(size)=$(

成員總大小) 按

$(整體對齊係數

) 圓整

= 8 /* 8%1=0 */ [注1]

2、2位元組對齊

(#pragma pack(2))

輸出結果:

sizeof(struct test_t) = 10 [

兩個編譯器輸出一致]

分析過程:

1) 成員資料對齊

#pragma pack(2)

struct test_t ;

#pragma pack()

成員總大小

=9

2) 整體對齊

整體對齊係數

= min((max(int,short,char), 2) = 2

整體大小

(size)=$(

成員總大小) 按

$(整體對齊係數

) 圓整

= 10 /* 10%2=0 */

3、4位元組對齊

(#pragma pack(4))

輸出結果:

sizeof(struct test_t) = 12 [

兩個編譯器輸出一致]

分析過程:

1) 成員資料對齊

#pragma pack(4)

struct test_t ;

#pragma pack()

成員總大小

=9

2) 整體對齊

整體對齊係數

= min((max(int,short,char), 4) = 4

整體大小

(size)=$(

成員總大小) 按

$(整體對齊係數

) 圓整

= 12 /* 12%4=0 */

4、8位元組對齊

(#pragma pack(8))

輸出結果:

sizeof(struct test_t) = 12 [

兩個編譯器輸出一致]

分析過程:

1) 成員資料對齊

#pragma pack(8)

struct test_t ;

#pragma pack()

成員總大小

=9

2) 整體對齊

整體對齊係數

= min((max(int,short,char), 8) = 4

整體大小

(size)=$(

成員總大小) 按

$(整體對齊係數

) 圓整

= 12 /* 12%4=0 */

5、16

位元組對齊

(#pragma pack(16))

輸出結果:

sizeof(struct test_t) = 12 [

兩個編譯器輸出一致]

分析過程:

1) 成員資料對齊

#pragma pack(16)

struct test_t ;

#pragma pack()

成員總大小

=9

2) 整體對齊

整體對齊係數

= min((max(int,short,char), 16) = 4

整體大小

(size)=$(

成員總大小) 按

$(整體對齊係數

) 圓整

= 12 /* 12%4=0 */

四、結論

8位元組和

16位元組對齊試驗證明了「規則

」的第3點:

「當#pragma pack的n

值等於或超過所有資料成員長度的時候,這個

n值的大小將不產生任何效果

」。另外記憶體對齊是個很複雜的東西,上面所說的在有些時候也可能不正確。呵呵

^_^ [

注1]

什麼是「圓整」

?舉例說明:如上面的

8位元組對齊中的

「整體對齊

」,整體大小

=9 按

4 圓整

= 12

圓整的過程:從

9開始每次加一,看是否能被

4整除,這裡9,

10,11均不能被

4整除,到

12時可以,則圓整結束。

也談記憶體對齊

一 記憶體對齊的原因 大部分的參考資料都是如是說的 1 平台原因 移植原因 不是所有的硬體平台都能訪問任意位址上的任意資料的 某些硬體平台只能在某些位址處取某些特定型別的資料,否則丟擲硬體異常。2 效能原因 資料結構 尤其是棧 應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需...

也談記憶體對齊 續

關於記憶體對齊的話題,始終是敏感的。稍有不慎,必將闖下大禍!最近專案稍顯輕閒,自己給自己安排一天反思和總結一下,突然想到以前寫過的一篇 也談記憶體對齊 那篇文章談的是記憶體對齊的基本知識以及一些實驗的資料,想必很多人看完後,會收穫一些東西,但是對記憶體對齊的應用還是處於懵懂狀態,其實大部分時間我們是...

也談C 記憶體區域

眾所周知,c 記憶體區域被分為5大類 棧 堆 自由儲存區 全域性 靜態儲存區 常量儲存區。棧由編譯器控制,棧空間的申請 使用和釋放全權由編譯器處理。這裡的 全權處理 意思是責任歸屬,並不是說編譯器在程式執行時介入管理。實際上,編譯器的工作在編譯期就完成了,它對棧的管理體現在編譯時對暫存器esp的維護...