**:
一,什麼是記憶體對齊?記憶體對齊用來做什麼?所謂記憶體對齊,是為了讓記憶體訪問更有效率而採用的一種編譯階段優化記憶體訪問的手段。
比如對於int x;(這裡假設sizeof(int)==4),因為cpu對記憶體的讀取操作是對齊的,如果x的位址不是4的倍數,那麼讀取這個x,需要讀取兩次共8個位元組,然後還要將其拼接成乙個int,這比訪問對齊過的x要麻煩很多。
二,怎麼算記憶體對齊大小(理論)?對於簡單型別,如int,char,float等,其對齊大小為其本身大小,即align(int) == sizeof(int),align(char)==sizeof(char),等等。
對於復合型別,如struct,class,其本身並無所謂對齊,因為cpu沒有直接訪問乙個struct的指令。對於struct而言,它的對齊指的是它裡面的所有成員變數都是對齊的,class同理。
下面就講講struct對齊是怎麼回事。
首先要明白三個點:
1,記憶體對齊是指首位址對齊,而不是說每個變數大小對齊;
2,結構體內存對齊要求結構體內每乙個成員變數都是記憶體對齊的;
3,結構體對齊除了第2點之外還要求結構體陣列也必須是對齊的,也就是說每個相鄰的結構體內部都是對齊的。
ok,先知道上面這3點之後,開始接觸怎麼算對齊大小。
程式設計師可自己指定某些資料的對齊大小,通過使用下面的預處理指令,指定對齊大小為x。(這裡需要注意:只能指定2的n次方作為對齊大小,對於指定對齊大小為6,9,10這樣的編譯器可能會不予理會)。
#pragma pack(x)
//...
#pragma pack()
那到現在,可能大家有個疑問了,那對於int(這裡假設sizeof(int)==4),手動指定對齊大小為8,那align(int)是等於sizeof(int)還是等於8呢 ?
這裡大家可以記住,align(x) = min ( sizeof(x) , packalign) , 即sizeof(x)和指定對齊大小哪個小,對齊大小就為哪個。
因此,上面的疑問答案是align(int)=sizeof(int)=4 。
三,怎麼算記憶體對齊大小(示範)?
#include
intmain
(int argc,
char
* ar**)
;#pragma pack()
assert
(sizeof
(test_a)==5
);//此處指定對齊大小為2
//對於a,實際對齊大小為min(sizeof(int),2)=min(4,2)=2
//對於b,實際對齊大小為min(sizeof(char),2)=min(1,2)=1
//編譯器會確保test_a首位址即a的地首址是2位元組對齊的,此時a對齊
//對於b,由於b要求首位址1位元組對齊,這顯然對於任何位址都合適,所以a,b都是對齊的
//對於test_b陣列,第乙個test_b是對齊的(假設其位址為0),則第二個test_b的首位址為(0+5=5),對於第二個test_b的變數a,顯然位址5是不對齊於2位元組的
//因此,需要在test_b的變數b後面填充1位元組,此時連續相連的test_b陣列才會對齊
//ok,對齊合理。因此整個結構體的大小為5+1=6
#pragma pack(2)
struct test_b
;#pragma pack()
assert
(sizeof
(test_b)==6
);//此處指定對齊大小為4
//對於a,實際對齊大小為min(sizeof(int),2)=min(4,4)=4
//對於b,實際對齊大小為min(sizeof(char),2)=min(1,4)=1
//編譯器會確保test_a首位址即a的地首址是4位元組對齊的,此時a對齊
//對於b,由於b要求首位址1位元組對齊,這顯然對於任何位址都合適,所以a,b都是對齊的
//對於test_c陣列,第乙個test_c是對齊的(假設其位址為0),則第二個test_c的首位址為(0+5=5),對於第二個test_c的變數a,顯然位址5是不對齊於4位元組的
//因此,需要在test_c的變數b後面填充3位元組,此時連續相連的test_c陣列才會對齊
//ok,對齊合理。因此整個結構體的大小為5+3=8
#pragma pack(4)
struct test_c
;#pragma pack()
assert
(sizeof
(test_c)==8
);//此處指定對齊大小為8
//對於a,實際對齊大小為min(sizeof(int),8)=min(4,8)=4
//對於b,實際對齊大小為min(sizeof(char),8)=min(1,8)=1
//編譯器會確保test_a首位址即a的地首址是4位元組對齊的,此時a對齊
//對於b,由於b要求首位址1位元組對齊,這顯然對於任何位址都合適,所以a,b都是對齊的
//對於test_d陣列,第乙個test_d是對齊的(假設其位址為0),則第二個test_d的首位址為(0+5=5),對於第二個test_d的變數a,顯然位址5是不對齊於4位元組的
//因此,需要在test_d的變數b後面填充3位元組,此時連續相連的test_d陣列才會對齊
//ok,對齊合理。因此整個結構體的大小為5+3=8
#pragma pack(8)
struct test_d
;#pragma pack()
assert
(sizeof
(test_d)==8
);//此處指定對齊大小為8
//對於a,實際對齊大小為min(sizeof(int),8)=min(4,8)=4
//對於b,實際對齊大小為min(sizeof(char),8)=min(1,8)=1
//對於c,這是乙個陣列,陣列的對齊大小與其單元一致,因而align(c)=align(double)=min(sizeof(double),8)=min(8,8)=8
//對於d,實際對齊大小為min(sizeof(char),8)=min(1,8)=1
//編譯器會確保test_a首位址即a的地首址是4位元組對齊的,此時a對齊
//對於b,由於b要求首位址1位元組對齊,這顯然對於任何位址都合適,所以a,b都是對齊的
//對於c,由於c要求首位址8位元組對齊,因此前面的a+b=5,還要在c後面補上3個位元組才能對齊
//對於d,顯而易見,任何位址均對齊,此時結構體大小為4+1+3+10*8+1=89
//對於test_e陣列,第乙個test_e是對齊的(假設其位址為0),則第二個test_e的首位址為(0+89=89),對於第二個test_e的變數a,顯然位址89是不對齊於4位元組的
//因此,需要在test_e的變數d後面填充7位元組,此時連續相連的test_e陣列才會對齊
//ok,對齊合理。因此整個結構體的大小為(4)+(1+3)+(10*8)+(1+7)=96
#pragma pack(8)
struct test_e
;#pragma pack()
assert
(sizeof
(test_e)
==96);
return0;
}
四,記憶體對齊相關使用msvc未公開編譯選項可以檢視c++類的記憶體布局。使用方法:啟動vs命令列,輸入cl 【source.cpp】 /d1reportsingleclasslayout【cbaseclass1】以檢視單個class的記憶體布局,輸入cl 【source.cpp】 /d1reportallclasslayout以檢視所有類的記憶體布局。注意:/d1reportsingleclasslayout【cbaseclass1】沒有空格 !!
大家可以用這個來對照我上面講的例子來看編譯器是怎麼安排對齊的。
這個東東是神器,類似於巨集展開時的選項(輸出與處理過之後的原始檔),一切內部布局方面的真相全都展現在你眼前,包括坑腦細胞的虛函式、虛函式表、虛基類表、虛繼承等一系列坑爹。
五,參考資料1,
2,3,
4,5,
記憶體對齊方式
看到這篇博文題目,你可能會想,什麼題目到底啥意思 首先,你在申請一塊空間時,系統會根據你所申請的空間大小,按照一定規則,給他分配一段空間的起始位址,然後你就可以安心的開始寫你的資料了。今天,這篇博文便是談一談,系統是按照怎麼樣的規則進行分配空間的。在正式開始之前,我們先小試牛刀 struct t1 ...
整數邊界對齊方式 C高階 記憶體對齊
碼字不易,對你有幫助點讚 關注支援一下作者不會程式設計的程式圓看更多乾貨,獲取第一時間更新如果想看比較好看的排版,可以閱讀原文 c高階 四 記憶體對齊 mp.weixin.qq.com 我們這一節主要來講一相關的些比較重要的知識。struct s1 上面是乙個結構體,也是我們自定義的一種型別。我們知...
C 中structure中記憶體對齊方式
原 2015年10月12日 01 19 24 cainv89 閱讀數 12402 標籤 結構體復合資料型別 c與c 結構體區別 結構體的作用 結構體的記憶體對齊方式更多 個人分類 c 基礎 2 示例 二 此宣告宣告了擁有3個成員的結構體,分別為整型的a,字元型的b和雙精度的c,但沒有標明其標籤,宣告...