c 中的記憶體對齊?

2021-10-08 19:53:56 字數 3610 閱讀 7033

struct test

;

對其執行sizeof(test),得到值為32,並且我們對裡面每個變數取sizeof,確實是所屬型別的大小,但為什麼不是1+4+1+8+1=15呢,我們執行下面**來計算每個變數之間的位址偏移量:

test t;

cout <<

sizeof

(t)<< endl;

cout <<

(unsigned

int)

(void*)

&t.m2 -

(unsigned

int)

(void*)

&t.m1 << endl;

cout <<

(unsigned

int)

(void*)

&t.m3 -

(unsigned

int)

(void*)

&t.m2 << endl;

cout <<

(unsigned

int)

(void*)

&t.m4 -

(unsigned

int)

(void*)

&t.m3 << endl;

cout <<

(unsigned

int)

(void*)

&t.m5 -

(unsigned

int)

(void*)

&t.m4 << endl;

輸出結果如下

3244

88

這是因為結構體內存分配有自己的對齊規則,結構體內存對齊預設的規則如下:

1、 分配記憶體的順序是按照宣告的順序。

2、 每個變數相對於起始位置的偏移量必須是該變數型別大小的整數倍,不是整數倍空出記憶體,直到偏移量是整數倍為止。

3、 最後整個結構體的大小必須是裡面變數型別最大值的整數倍。

分析上面test結構體

1

、分配m1,此時偏移量是

0,整數倍,分配

1個位元組2、

分配m2,此時偏移量為

1,int型大小為

4,不是整數倍,因此先跳過

3個位元組,此時偏移量為

4,達到整數倍,分配m2的四個位元組,因此這個步驟分配了

7個位元組3、

分配m3

,此時的偏移量為

8,m3只需要

1個位元組,整數倍,分配

1個位元組4、

分配m4,此時偏移量為

9,double型是

8個位元組,不是整數倍,下乙個整數倍是

16,因此先跳過

7個位元組,再分配m4,該步驟共分配了

15個位元組。5、

分配m5,此時的偏移量為

24,是整數倍,分配

1個位元組6、

此時一共分配了

25個位元組,但總大小要是最大型別大小的整數倍,double為最大型別,是

8,最近的是

32,因此該步驟要再分配

7個位元組。

當然上面只是編譯器預設的分配規則,我們可以通過下面幾個方法改變結構體的大小

1、 改變結構體中變數的宣告順序,按照型別大小從小到大的順序宣告,占用的空間就會比較小。

2、 我們可以使用#pragma修改這個規則, #pragma是c++的乙個預處理指令,它有很多作用,其中乙個作用就是修改分配規則。在上面**的結構體定義前面新增:

#pragma pack(1)

執行結果如下151

418

改為#pragma pack(2

)時如下182

428

新增了#pragma pack(n)後規則就變成了下面這樣:

1、 偏移量要是n和當前變數大小中較小值的整數倍

2、 整體大小要是n和最大變數大小中較小值的整數倍

3、 n值必須為1,2,4,8…,為其他值時就按照預設的分配規則

注意:其實最開始的例子也是按照這樣的規則,只是它使用的是系統預設的n值,預設為8,vs中的路徑為project]|[settings],c/c++選項卡category的code generation選項的struct member alignment。

#pragma pack的常用用法如下

1、#pragma pack(push, n) //將當前對齊值值設為n,並將之前的對齊值壓棧儲存

2、#pragma pack(n)//將當前對齊值設為n,不儲存之前值

3、#pragma pack()//將當前對齊值恢復到預設8

4、#pragma pack(pop)//如果棧有值,就以棧頂值出棧並設為為當前值,棧裡沒有值否則就不變

5、#pragma pack(pop, n)// 如果棧有值,就以棧頂值出棧,將當前對齊值設為n

除了結構體,聯合和類也是這樣的。

為什麼要對齊呢

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

對齊的作用和原因:各個硬體平台對儲存空間的處理上有很大的不同。一些平台對某些特定型別的資料只能從某些特定位址開始訪問。其他平台可能沒有這種情況,但是最常見的是如果不按照適合其平台要求對資料存放進行對齊,會在訪問效率上帶來損失。比如有些平台每次讀都是從偶位址開始,如果乙個int型(假設為32位系統)如果存放在偶位址開始的地方,那麼乙個讀週期就可以讀出,而如果存放在奇位址開始的地方,就可能會需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該int資料。顯然在讀取效率上下降很多。這也是空間和時間的博弈。一般我們寫程式的時候,不需要考慮對齊問題。編譯器會替我們選擇適合目標平台的對齊策略。

**中關於對齊的隱患,很多是隱式的。比如在強制型別轉換的時候。例如:

unsigned

int i =

0xffffffff

; cout << hex << i << endl;

char

*p =

null

;unsigned

short

*p1=

null

;//p= reinterpret_cast(&i);

p=(char*)

(&i)

;*p=

0x00

; cout << hex << i << endl;

p1=(unsigned

short*)

(p+1);

*p1=

0x0000

; cout << hex << i << endl;

輸出結果如下

ffffffff

ffffff00

ff000000

最後兩句**,從奇數邊界去訪問unsignedshort型變數,顯然不符合對齊的規定。

C 中的記憶體對齊

記憶體對齊 在我們的程式中,資料結構還有變數等等都需要占有記憶體,在很多系統中,它都要求記憶體分配的時候要對齊,這樣做的好處就是可以提高訪問記憶體的速度。我們還是先來看一段簡單的程式 程式一 1 include 2using namespace std 34 structx15 1011struct...

C 中的記憶體對齊

在我們的程式中,資料結構還有變數等等都需要占有記憶體,在很多系統中,它都要求記憶體分配的時候要對齊,這樣做的好處就是可以提高訪問記憶體的速度。我們還是先來看一段簡單的程式 程式一 1 include iostream 2using namespace std 34 structx15 1011str...

C 中的記憶體對齊

c 的一道題 include iostream include string using namespace std pragma pack 1 struct student stu struct s s void main stu 結構體對齊 最後的偏移位址25不是4的倍數,填充3個位元組後,滿足...