必須注意:對齊是多少位元組對齊,不是多少位對齊。
對齊原因:
如上,記憶體一般是四個單位一列,cpu在讀取記憶體資料的時候,通過匯流排並行讀取每個單位的資料。對於cpu 32bit的暫存器而言。
而記憶體結構是,記憶體位址乙個單位也就是基本單位是1位元組,也就是8位二進位制數。
在讀取資料的時候實際上是0-7位從晶元0讀取,8到15位從晶元2讀取,以此類推。
所以讀取乙個4位元組資料,是四個晶元同時往暫存器裡面讀。
如下圖:
左邊是位址偏移。用於匯流排高效傳輸位址,晶元上的數字是記憶體位址,每個記憶體位址需要讀取幾位,就在本晶元上向後讀取幾位。注意這裡說的是實體地址,而不是虛擬位址,實體地址不是線性的,是混亂的。
如果讀取4位int,就會從晶元0讀取8bits填充暫存器的0--7bit,晶元1讀取8bits填充8--15bits,以此類推。
如果資料沒有對齊,要從位址1讀取4位元組,讀取進去之後,需要cpu迴圈左移8bits來達到正確。但是cpu在讀取資料的是後需要向記憶體傳送位址,如果沒有對齊,傳送的位址偏移(左邊的位址偏移)量是不同的,1、2、3位址偏移為0,而0晶元接受的位址偏移卻為1,這樣一來,傳送位址就無法使用1根匯流排來完成了,而是需要4個匯流排,每個位址匯流排需要32個cpu引腳,而32bitcpu總共才400多個引腳,這樣的設計,增加硬體設計複雜性,是不必要的。如果不改變設計複雜性,訪問未對齊資料就需要兩次訪問才能得到資料。
例如:訪問位址是1、2、3、4的4位元組資料,先要讀取位址0-3的資料,在讀取4--7的資料,再把1--4的資料取出來放入目標暫存器。所以不對其就要多次訪問記憶體。某些cpu比如power pc直接禁止訪問不對其資料。
總結一下為什麼需要資料對齊。為了增加cpu讀取資料的頻寬,記憶體系統通常都採用並 行結構使得可以並行傳輸資料。這樣的並行結構使得訪問對齊的資料速度快,但是若 要使訪問不對奇的資料也一樣快會使cpu與記憶體系統的介面變得更複雜,而這是划不來 的。經過權衡之後,最終的結果是:訪問對齊的資料速度快,訪問不對奇的資料速度 慢(需要2次訪問)或乾脆禁止訪問不對奇資料。
對於基本 型別(不包括結構體,聯合體和陣列)不同的資料型別的對齊模數通常等於這個資料 型別的大小,比如char的對齊模數為1,short的對齊模 數為2,int的對齊模數為4,float的對齊模數是 4,double的對齊模數為8
當然,這個對齊模數每乙個編譯器可能有差 異,並且可以設定。在visual c++中可以通過/zp選項 獲#pragma設定。
綜合起來,基本型別的對齊模數是這個型別的大小和 設定的對齊限制的較小者。對於結構體,聯合體和陣列,對齊模數是它的成員的對齊 模數的最大值。
結構體對齊:
__declspec( align(#) )和#pragma pack( n )
一、#pragma pack(n)
#pragma pack ( 16 )
typedef struct test;
結構體中的資料成員,除了第乙個是始終放在最開始的地方,其它資料成員的位址必須是它本身大小或對齊引數兩者中較小的乙個的倍數。
結構體的資料成員的位址必須是本身大小和對齊引數中較小的那乙個。
簡單型別組成的結構體,大小由每個成員變數的位址決定。按照定義順序,分別求出位址開始的地方,從位址0開始,每個變數都採取min(對齊引數,sizeof(type))來確定起始位址是多少的倍數,然後填滿該資料,最後求出總大小。在pack大等於2的時候,最終大小要是2的倍數,需要向上取到為2的倍數。pack為1則不需要。
對於含有結構體的結構體,方法同上,結構體變數的對其引數為min(pack對齊引數,結構體成員最大元素大小)。
__declspec( align(#) )
當乙個變數或結構體同時受__declspec( align(#) )和#pragma pack( n )影響時,前者的優先順序高。
min(n,m,sizeof(成員變數型別));
結構體的最終大小要麼是__declspec( align(m) )中m的倍數,要麼是結構體中最大型別大小的倍數,去最大。即:
max(m,sizeof(最大型別大小));
結構體中有結構體的例子:
#include #include最後執行的結果是sizeof(e)為96,為何會是這個結果呢?"stdafx.h
"#include
//using namespace std;
#pragma pack( push, 4 )__declspec( align(
32) )structd;
__declspec( align(
16) ) structe;
intmain()
對於結構體e,第乙個元素為int型別,所以佔據[0~3]位址單元。
第二個元素是乙個結構體,該結構體由於受上面__declspec( align(32) )的影響,優先順序高,所以起始位址是32的倍數,而且大小為32b,從而應該放置在[32~63]單元處。
最後乙個是int型別的變數,大小為4,所以應該是4的倍數,位址為[64~67]。
故結構體e的大小應該是從[0~67],佔據68b,而由於前面還有限制__declspec( align(16) ),同時成員變數的最大偏移是sizeof(d)=32,所以我們最後這個結構體的大小應該是他們中最大值的倍數,也就是32的倍數,68向上取32的倍數應該是96.故結果為96.
注意:_declspec ( align() )的乙個特點是,它僅僅規定了資料對齊的位置,而沒有規定資料實際占用的記憶體長度,
當指定的資料被放置在確定的位置之後,其後的資料填充仍然是按照#pragma pack規定的方式填充的。
特別注意:不要忘記__declspec ( align(n) )會讓結構體的首位址n位元組對齊。
位元組對齊總結
寫在前面位元組對其是面試筆試當中出現頻率算是比較高的乙個知識點。在此總結一下。主要內容例題 pragma pack 2 class bu ubuf void foo typedef char f void enum disk bu 問 sizeof bu 的值是 首先先不急著回答上面的問題如果你不知...
位元組對齊總結
1.位元組對齊的緣由?一句話說就是提高訪問效率,訪問效率與機器相關,比如有的機器總是從偶數位元組開始取資料 同時,訪問效率也和資料型別相關,比如如果取乙個整型數字時,若能夠一次取出來肯定是最好,但是若由於機器取資料總是從偶數位元組開始這個原因花了兩次訪問,那麼肯定會降低效率了。2.字元對齊中的一些概...
ARM 記憶體對齊總結
一 啥是記憶體對齊?為啥要記憶體對齊?現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問都可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體位址訪問,這就是對齊。位元組對齊的原因大致是如下兩條 1 平台原因 移植原因 不是所有的硬體平台都能訪問...