結構體字節對齊
在用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
{
int
a;
char
b;
short
c;
}S1;
|
則sizeof(S1)=8。這是因為結構體node1中最長的數據類型是int,占4個字節,因此以4字節對齊,則該結構體在內存中存放方式為
|--------int--------| 4字節
|char|----|--short-| 4字節
總共占8字節
3.test3
typedef
struct
node2
{
char
a;
int
b;
short
c;
}S2;
|
則siezof(S3)=12.最長數據類型為int,占4個字節。因此以4字節對齊,其在內存空間存放方式如下:
|char|----|----|----| 4字節
|--------int--------| 4字節
|--short--|----|----| 4字節
總共占12個字節
4.test4 含有靜態數據成員
typedef
struct
node3
{
int
a;
short
b;
static
int
c;
}S3;
|
則sizeof(S3)=8.這里結構體中包含靜態數據成員,而靜態數據成員的存放位置與結構體實例的存儲地址無關(注意只有在C++中結構體中才能含有靜態數據成員,而C中結構體中是不允許含有靜態數據成員的)。其在內存中存儲方式如下:
|--------int--------| 4字節
|--short-|----|----| 4字節
而變量c是單獨存放在靜態數據區的,因此用siezof計算其大小時沒有將c所占的空間計算進來。
5.test5 結構體中含有結構體
typedef
struct
node4
{
bool
a;
S1 s1;
short
b;
}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
{
bool
a;
S1 s1;
double
b;
int
c;
}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
{
bool
a;
S1 s1;
double
b;
int
c;
}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 <iostream>
using
namespace
std;
//#pragma pack(4) //設置4字節對齊
//#pragma pack() //取消4字節對齊
typedef
struct
node
{
}S;
typedef
struct
node1
{
int
a;
char
b;
short
c;
}S1;
typedef
struct
node2
{
char
a;
int
b;
short
c;
}S2;
typedef
struct
node3
{
int
a;
short
b;
static
int
c;
}S3;
typedef
struct
node4
{
bool
a;
S1 s1;
short
b;
}S4;
typedef
struct
node5
{
bool
a;
S1 s1;
double
b;
int
c;
}S5;
int
main(
int
argc,
char
*argv[])
{
cout<<
sizeof
(
char
)<<
" "
<<
sizeof
(
short
)<<
" "
<<
sizeof
(
int
)<<
" "
<<
sizeof
(
float
)<<
" "
<<
sizeof
(
double
)<<endl;
S s;
S1 s1;
S2 s2;
S3 s3;
S4 s4;
S5 s5;
cout<<
sizeof
(S3)<<endl;
cout<<
sizeof
(s)<<
" "
<<
sizeof
(s1)<<
" "
<<
sizeof
(s2)<<
" "
<<
sizeof
(s3)<<
" "
<<
sizeof
(s4)<<
" "
<<
sizeof
(s5)<<endl;
return
0;
}