天天看點

struct中的記憶體對齊 && union

http://www.cnblogs.com/fly1988happy/archive/2012/04/12/2444408.html

做題的時候被記憶體對齊困擾了很久,終于找到一個能讓自己了解透徹的回答,希望對大家有幫助

1.C語言中的結構體

1.1 定義

結構體是由一系列相同或不同類型的變量組成的集合。

struct 結構體名{               //struct為關鍵字,“結構體名”為使用者定義的類型辨別。

資料類型1 成員名1;          //{ }中是組成該結構體的成員,其中資料類型可以是C語言所允許的任何資料類型。

資料類型2 成員名2;

...

資料類型n 成員名n;

};

1.2 結構體的記憶體配置設定(方法一)

結構體在記憶體中配置設定一塊連續的記憶體,但結構體内的變量并不一定是連續存放的,這涉及到記憶體對齊。

原則1  資料成員對齊規則:結構(struct或聯合union)的資料成員,第一個資料成員放在offset為0的地方,以後每個資料成員存儲的起始位置要從該成員大小的整數倍開始(比如int在32位機為4位元組,則要從4的整數倍位址開始存儲)。

原則2  結構體作為成員:如果一個結構裡有某些結構體成員,則結構體成員要從其内部最大元素大小的整數倍位址開始存儲。(struct a裡存有struct b,b裡有char,int,double等元素,那b應該從8的整數倍開始存儲。)

原則3  收尾工作:結構體的總大小,也就是sizeof的結果,必須是其内部最大成員的整數倍,不足的要補齊。

例1.

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

sizeof(A) = 8; int為4,char為1,short為2,這裡用到了原則1和原則3。
sizeof(B) = 12; char為1,int為4,short為2,怎麼會是12?還是原則1和原則3。

             a         b         c
A的記憶體布局:1111,     1*,        11

             b          a        c
B的記憶體布局:1***,     1111,     11**
           

其中星号*表示填充的位元組。

A中,b後面為何要補充一個位元組?因為c為short,其起始位置要為2的倍數,就是原則1。c的後面沒有補充,因為b和c正好占用4個位元組,整個A占用空間為4的倍數,也就是最大成員int類型的倍數,是以不用補充。

B中,b是char為1,b後面補充了3個位元組,因為a是int為4,根據原則1,起始位置要為4的倍數,是以b後面要補充3個位元組。c後面補充兩個位元組,根據原則3,整個B占用空間要為4的倍數,c後面不補充,整個B的空間為10,不符,是以要補充2個位元組。

例2.

struct A                              struct B
{                                     {
    int a;                                char e[2];
    double b;                             int f;
    float c;                              double g;
};                                        short h;
                                          struct A i;
                                      };

sizeof(A) = 24; int為4,double為8,float為4,總長為8的倍數,補齊,是以整個A為24。

sizeof(B) = 48; 看看B的記憶體布局。

            e         f       g              h           
B的記憶體布局:11* *,   1111,   11111111, 11 * * * * * *,        

            i 
           1111* * * *, 11111111, 1111 * * * *
           

  i其實就是A的記憶體布局。根據原則2,i的起始位置要為8的倍數,是以h後面要補齊。

1.3 結構體的記憶體配置設定(方法二)

struct的記憶體大小為每個資料記憶體的加和,首先按照最大的資料類型進行單個配置設定,如果前一個資料占用不了所有的記憶體,而剩下的記憶體可以放下下一個資料,則第二個資料不另外配置設定記憶體(但是位址必須是從這個資料類型大小的整數倍開始,看下面的struct C),否則重新配置設定一個最大類型的記憶體。(個人覺得這種方法比較好了解!)

例3.

struct A                           struct B                           struct C
{                                  {                                  {
    int a;                             int a;                             int a;
    char b;                            double b;                          char b;
    double c;                          char c;                            short c;
};                                 };                                    char d;
                                                                      };
           

對于結構體A: 

因為A中最大的資料類型是double,占8個位元組。是以系統先配置設定8個位元組用來放int,結果int隻需要4個就夠了,然後剩下的4個位元組中的1個可以用來放後面的char,碰到double c時,因為此時的3個位元組不能存下,是以再配置設定了一個8個位元組來存放double c。是以A占用16個位元組。

對于結構體B:

系統碰到int分給他8個位元組存放,碰到double時,剩下的4個位元組不足以存放,是以再配置設定了8個位元組,再遇到char時又配置設定了8個位元組,這樣B共配置設定了24個位元組。(系統其實是浪費了5個位元組的空間)

比較A和B,隻有變量定義的順序不一樣,結果占用的記憶體空間也不一樣。是以,結構體裡面最好按照類型從小到大的順序來排列,以免浪費空間。

對于結構體C:

按照上述方法,最大的資料類型是int,占4個位元組,系統先配置設定4個位元組(0~3);再配置設定4個位元組(4~7),存放char b;short c占2個位元組,但是必須從2的整數倍開始,是以應當配置設定(6~7),中間空餘1個位元組;char d占1個位元組,但是上次配置設定的4位元組用完了,是以需要再配置設定4個位元組存放char d,d隻占用1個位元組,是以剩下3個位元組空閑。sizeof(struct C)=12。

 2. C++中的結構體與類的差別

C中的結構體不允許有函數,而C++中的結構體允許。

類與結構體在C++中隻有兩點差別,除此這外無任何差別。

1)class中預設的成員通路權限是private的,而struct中則是public的。   

2)從class繼承預設是private繼承,而從struct繼承預設是public繼承。

3. 聯合union

3.1 定義

聯合(又叫共用體)是一種特殊形式的變量,使用關鍵字union來定義 ,它的聲明與變量定義與結構體十分相似。其形式為:   

union 聯合名

{    

資料類型 成員名;    

資料類型 成員名;    

...   

} 變量名;

聯合表示幾個變量共用一個記憶體位置,在不同的時間儲存不同的資料類型和不同長度的變量。在union中,所有的聯合成員共用一個空間,并且同一時間隻能儲存其中一個成員變量的值。

3.2 聯合的記憶體配置設定

Union的大小為其内部所有變量的最大值,并且按照類型最大值的整數倍進行記憶體對齊。

union A            union B             union C                  union D
{                  {                   {                        {
    char c[10];        char c[10];         char c[10];              char c;
    char cc1;          int  i;             double d;                int i;
}u1;               }u2;                }u3;                         double d;
                                                                 }u4;
           

union A :首先按照char c[10]配置設定10個位元組,然後按照char的1個位元組對齊,最終sizeof(u1)=10;

union B :首先按照char c[10]配置設定10個位元組,然後按照int的4個位元組對齊,最終sizeof(u2)=12; (大于等于10且能被4整除的最小的數,即12)

union C :首先按照char c[10]配置設定10個位元組,然後按照doube的8個位元組對齊,最終sizeof(u3)=16;(大于等于10且能被8整除的最小的數,即16)

union D:按照double配置設定8個位元組,最終sizeof(u4)=8;

3.3 應用

在C/C++程式的編寫中,當多個基本資料類型或複合資料結構要占用同一片記憶體時,我們要使用聯合體;當多種類型,多個對象,多個事物隻取其一時(我們姑且通俗地稱其為“n 選1”),我們也可以使用聯合體來發揮其長處。

union myun 
{
    struct 
    { 
        int x; 
        int y; 
        int z; 
    }u; 
    int k; 
}a; 

int main() 
{ 
    a.u.x =4;
    a.u.y =5; 
    a.u.z =6; 
    a.k = 0; 
    printf("%d %d %d\n",a.u.x,a.u.y,a.u.z);
    return 0;
}
           

union類型是共享記憶體的,以size最大的結構作為自己的大小,這樣的話,myun這個結構就包含u這個結構體,而大小也等于u這個結構體的大小,在記憶體中的排列為聲明的順序x,y,z從低到高,然後指派的時候,在記憶體中,就是x的位置放置4,y的位置放置5,z的位置放置6,現在對k指派,對k的指派因為是union,要共享記憶體,是以從union的首位址開始放置,首位址開始的位置其實是x的位置,這樣原來記憶體中x的位置就被k所賦的值代替了,就變為0了,這個時候要進行列印,就直接看記憶體裡就行了,x的位置也就是k的位置是0,而y,z的位置的值沒有改變,是以應該是0,5,6。

 4.結構體和聯合的差別:

1)聯合和結構體都是由多個不同的資料類型成員組成,但在任何同一時刻,聯合隻存放了一個被選中的成員,而結構體的所有成員都存在。   

2)對于聯合的不同成員指派,将會對其它成員重寫,原來成員的值就不存在了,而對于結構體的不同成員指派是互不影響的。