天天看點

struct union資料對齊和sizeof大小

什麼是資料對齊?簡單的說就是資料的起始位址必須是對齊值的整數倍。如果對齊值為N,則 起始位址 % N=0。

為什麼要資料對齊?為了提高存取變量的效率。位元組是記憶體空間配置設定的最小機關, 在程式中,我們定義的變量可以放在任何位置。其實不同架構 的CPU在通路特定類型變量時是有規律的,比如有的CPU通路int型變量時,會從偶數位址開始讀取的,int類型占用4個位元組(windows平台)。 0X0000,0X0004,0X0008…..這樣隻需要讀一次就可以讀出Int類型變量的值。相反地,則需要讀取二次,再把高低位元組相拼才能得到 int類型的值,這樣子看的話,存取效率當然提高了。  

通常寫程式的時候,不需要考慮這些情況,編譯都會為我們考慮這些情況,除非針對那些特别架構的 CPU程式設計的時候的則需要考慮 。當然使用者也可以手工控制對齊方式。

資料對齊可以分為幾種類别:

1 基本資料類型的資料對齊

基本類型的資料對齊值是其本身的大小。

類型 對齊值(位元組)
char 1
short 2
int 4
float 4
double 8
指針 4(32位) 8(64位)

2 struct/class的自身對齊值。對于結構體和類的自身對齊值是所有成員中最大的自身對齊值。

結構體和類的對齊規則:先将資料成員對齊,在将結構體和類自身對齊,最終大小與資料成員順序 有關。

3 union的自身對齊值。union的自身對齊值是所有成員中最大的對齊值。union的對齊規則,隻需要union自身對齊,不需要資料成員對齊,最終大小與資料成員順序無關。

4 指定對齊值。使用#pragma pack(n)指定對齊值為n,使用#pragma pack() 回複預設對齊值。

5 有效對齊值。對于指定了對齊值的代碼, 有效對齊值=min(類/結構體/成員的自身對齊值, 指定對齊值)

未指定對齊值時,預設的對齊值一般為8。

有效對齊值決定了資料存放的方式,sizeof運算符是根據有效對齊值計算大小的。

舉例分析:

union U1
{
    char a[];
    int b;
    double c;
};

union U2
{
    int b;
    char a[];
    double c;
};

union U3
{
    double c;
    int b;
    char a[];

};
           

U1,U2,U3的sizeof的值都是16。 union的大小是所有成員中記憶體最大的大小。是以是9。因為對齊問題,union自身的對齊值是所有成員對齊的最大值 8。是以要填充 9個位元組, 最終的大小為16。

從這個例子可以看出:union不需要成員資料對齊, 最終大小與成員的順序無關。

struct A
{
    char a[];
    short b;
    int c;
};

struct B
{
    short b;
    int c;
    char a[];

};

struct C
{
    int c;
    char a[];
    short b;
};
           

結果值分别為16 20 16。

先看A,首先是成員的對齊。 a需要9個位元組,char的對齊值是2,是以填充1個位元組,9 + 1 + 2 = 12 個位元組。int的對齊值為4,12 % 4 = 0,是以 大小為 12 + 4 = 16。在看 struct的對齊。struct的對齊值為最大對齊值4。 16 % 4 = 0。 最終大小為16。

在看B,首先是成員的對齊。short需要2位元組,int的對齊值為4,是以填充2個位元組,2 + 2 + 4 = 8。 char的對齊值為1(與數組無關,不是9), 8 + 9 = 17。再看struct的對齊值為4,填充3個位元組,17 + 3 = 20。

再看C, int 需要4個位元組, a 需要9個位元組, 4 + 9 = 13。 short需要2個位元組, 4 + 9 + 1 + 2 = 16。 再看struct的對齊,對齊值為4, 16 % 4 = 0。 最終結果為4。

從這個例子可以看出:struct需要先對其資料成員,在對其struct本身。最終大小與成員順序有關。

書寫結構體時,建議按照資料類型(對齊值)從小到大書寫。

#pragma pack()

union U1
{
    char a[];
    int b;
    double c;
};

union U2
{
    int b;
    char a[];
    double c;
};

union U3
{
    double c;
    int b;
    char a[];

};

struct A
{
    char a[];
    short b;
    int c;
};

struct B
{
    short b;
    int c;
    char a[];

};

struct C
{
    int c;
    char a[];
    short b;
};
           

結果值分别為10 10 10 16 16 16.

先看三個union。大小都為9,成員中最大的對齊值為8,指定的對齊值為2, 有效對齊值為2。union本身對齊值為2。 則 結果為9+1=10。

再看A。a需要9個位元組,short需要2個位元組,填充一個位元組,9+1+2= 12。int的對齊值為4,指定為2,有效對齊值為2。 是以 12+4 =16。再看結構體本身,成員最大對齊值為4,指定為2, 有效對齊值為2, 16 % 2 = 0。最終結果為 16。

再看B。short需要2位元組,int有效對齊值為2,2 + 4 = 6, char需要9位元組, 6+ 9=15。 再看struct本身對齊。有效對齊值為2, 15+ 1 = 16。

再看C。int 占4位元組,數組占9位元組, 4+ 9 = 13, 填充一個位元組, 13+1 + 2 = 16. 再看struct本身對齊。滿足要求。最終結果為16。

如果是嵌套的情況呢?

舉例分析:

union U
{
    char a[];
    int b;
    double c;
};

struct A
{
    U u;
    char a[];
    short b;
    int c;
};

struct B
{
    short b;
    U u;
    int c;
    char a[];

};
struct C
{
    int c;
    char a[];
    short b;
    struct A aa;
};
           

A B C最終大小為:32 40 48;

分析:根據前面的分析,union U的大小為16,對齊值為 8。

A: U大小為16, char數組大小為9, 16 + 9 = 25。填充1個位元組,放short, 25 + 1 + 2 = 28。 再放int, 28+4=32。struct本身的對齊值為8, 32 % 8 = 0,滿足要求。

B:short占2位元組, U占16,最氣質為8,是以填充6位元組, 2 + 6 + 16 = 24。放int, 24 + 4 = 28。放char數組,28+9=37。 struct本身有效對齊值為8,填充3位元組, 37+3 = 40.

C: A的大小為32, 有效對齊值為8。 int 占4位元組, char數組占9位元組,4 +9=13。填充一個位元組,放short,13+1+2 = 16。 放A,16+32 = 48。struct本身對齊也滿足。

總結:對于嵌套類型,先按照規則分别計算每個資料成員的大小和對齊值,在考慮結構體本身的對齊。

繼續閱讀