在用sizeof運算子求算某結構體所佔空間時,並不是簡單地將結構體中所有元素各自佔的空間相加,這裡涉及到記憶體位元組對齊的問題。從理論上講,對於任何 變數的訪問都可以從任何位址開始訪問,但是事實上不是如此,實際上訪問特定型別的變數只能在特定的位址訪問,這就需要各個變數在空間上按一定的規則排列, 而不是簡單地順序排列,這就是記憶體對齊。
記憶體對齊的原因:
1)某些平台只能在特定的位址處訪問特定型別的資料;
2)提高訪問資料的速度。比如有的平台每次都是從偶位址處讀取資料,對於乙個int型的變數,若從偶位址單元處存放,則只需乙個讀取週期即可讀取該變數;但是若從奇位址單元處存放,則需要2個讀取週期讀取該變數。
win32平台下的微軟c編譯器對齊策略:
1)結構體變數的首位址能夠被其最寬資料型別成員的大小整除。編譯器在為結構體變數開闢空間時,首先找到結構體中最寬的資料型別,然後尋找記憶體位址能被該資料型別大小整除的位置,這個位置作為結構體變數的首位址。而將最寬資料型別的大小作為對齊標準。
2)結構體每個成員相對結構體首位址的偏移量(offset)都是每個成員本身大小的整數倍,如有需要會在成員之間填充位元組。編譯器在為結構體成員開闢空 間時,首先檢查預開闢空間的位址相對於結構體首位址的偏移量是否為該成員大小的整數倍,若是,則存放該成員;若不是,則填充若干位元組,以達到整數倍的要 求。
3)結構體變數所佔空間的大小必定是最寬資料型別大小的整數倍。如有需要會在最後乙個成員末尾填充若干位元組使得所佔空間大小是最寬資料型別大小的整數倍。
下面看一下sizeof在計算結構體大小的時候具體是怎樣計算的
1.test1 空結構體
typedef
struct
node
s;
則sizeof(s)=1;或sizeof(s)=0;
在c++中佔1位元組,而在c中佔0位元組。
2.test2
typedef
struct
node1
s1;
則sizeof(s1)=8。這是因為結構體node1中最長的資料型別是int,佔4個位元組,因此以4位元組對齊,則該結構體在記憶體中存放方式為
|--------int--------| 4位元組
|char|----|--short-| 4位元組
總共佔8位元組
3.test3
typedef
struct
node2
s2;
則siezof(s3)=12.最長資料型別為int,佔4個位元組。因此以4位元組對齊,其在記憶體空間存放方式如下:
|char|----|----|----| 4位元組
|--------int--------| 4位元組
|--short--|----|----| 4位元組
總共佔12個位元組
4.test4 含有靜態資料成員
typedef
struct
node3
s3;
則sizeof(s3)=8.這裡結構體中包含靜態資料成員,而靜態資料成員的存放位置與結構體例項的儲存位址無關(注意只有在c++中結構體中才能含有靜態資料成員,而c中結構體中是不允許含有靜態資料成員的)。其在記憶體中儲存方式如下:
|--------int--------| 4位元組
|--short-|----|----| 4位元組
而變數c是單獨存放在靜態資料區的,因此用siezof計算其大小時沒有將c所佔的空間計算進來。
5.test5 結構體中含有結構體
typedef
struct
node4
s4;
則sizeof(s4)=16。是因為s1佔8位元組,而s1中最長資料型別為int,佔4個位元組,bool型別1個位元組,short佔2位元組,因此以4位元組對齊,則儲存方式為
|-------bool--------| 4位元組
|-------s1----------| 8位元組
|-------short-------| 4位元組
6.test6
typedef
struct
node5
s5;
則sizeof(s5)=32。是因為s1佔8位元組,而s1中最長資料型別為int,佔4位元組,而double佔8位元組,因此以8位元組對齊,則存放方式為:
|--------bool--------| 8位元組
|---------s1---------| 8位元組
|--------double------| 8位元組
|----int----|---------| 8位元組
7.test7
若在程式中使用了#pragma pack(n)命令強制以n位元組對齊時,預設情況下n為8.
則比較n和結構體中最長資料型別所佔的位元組大小,取兩者中小的乙個作為對齊標準。
若需取消強制對齊方式,則可用命令#pragma pack()
如果在程式開頭使用命令#pragma pack(4),對於下面的結構體
typedef
struct
node5
s5;
則sizeof(s5)=24.因為強制以4位元組對齊,而s5中最長資料型別為double,佔8位元組,因此以4位元組對齊。在記憶體中存放方式為:
|-----------a--------| 4位元組
|--------s1----------| 4位元組
|--------s1----------| 4位元組
|--------b-----------| 4位元組
|--------b-----------| 4位元組
|---------c----------| 4位元組
總結一下,在計算sizeof時主要注意一下幾點:
1)若為空結構體,則只佔1個位元組的單元
2)若結構體中所有資料型別都相同,則其所佔空間為 成員資料型別長度×成員個數
若結構體中資料型別不同,則取最長資料型別成員所佔的空間為對齊標準,資料成員包含另乙個結構體變數t的話,則取t中最 長資料型別與其他資料成員比較,取最長的作為對齊標準,但是t存放時看做乙個單位存放,只需看其他成員即可。
3)若使用了#pragma pack(n)命令強制對齊標準,則取n和結構體中最長資料型別佔的位元組數兩者之中的小者作為對齊標準。
另外除了結構體中存在對齊之外,普通的變數儲存也存在位元組對齊的情況,即自身對齊。編譯器規定:普通變數的儲存首位址必須能被該變數的資料型別寬度整除。
測試程式:
/*測試sizeof運算子 2011.10.1*/
#include
using
namespace
std;
//#pragma pack(4) //設定4位元組對齊
//#pragma pack() //取消4位元組對齊
typedef
struct
node
s;
typedef
struct
node1
s1;
typedef
struct
node2
s2;
typedef
struct
node3
s3;
typedef
struct
node4
s4;
typedef
struct
node5
s5;
int
main(
int
argc,
char
*argv)
結構體位元組對齊
include pragma pack 2 struct t.pragma pack int main int argc,char argv 最後輸出的結果為 8。這個表示是按照2位元組來對齊資料,首先分配2位元組給成員變數i,分配完成後,還剩一位元組,zj add補0 沒法容納成員變數d,此時會再...
結構體位元組對齊
include pragma pack 2 struct t.pragma pack int main int argc,char argv 最後輸出的結果為 8。這個表示是按照2位元組來對齊資料,首先分配2位元組給成員變數i,分配完成後,還剩一位元組,zj add補0 沒法容納成員變數d,此時會再...
結構體位元組對齊
結構體位元組對齊 1 什麼是位元組對齊 乙個變數占用 n 個位元組,則該變數的起始位址必須能夠被 n 整除,即 存放起始位址 n 0,對於結構體而言,這個 n 取其成員種的資料型別佔空間的值最大的那個。2 為什麼要位元組對齊 記憶體空間是按照位元組來劃分的,從理論上說對記憶體空間的訪問可以從任何位址...