Struct 和 Union區別

原文地址:http://blog.csdn.net/firefly_2002/article/details/7954458

本人菜鳥,學習中,最後有改動,自我理解部分,如有錯誤,請參考原文或通知我,不勝感激

一、Struct 和 Union有下列區別:

1.在存儲多個成員信息時,編譯器會自動給struct第個成員分配存儲空間,struct 可以存儲多個成員信息,而Union每個成員會用同一個存儲空間,只能存儲最後一個成員的信息

2.都是由多個不同的數據類型成員組成,但在任何同一時刻,Union只存放了一個被先選中的成員,而結構體的所有成員都存在

3.對於Union的不同成員賦值,將會對其他成員重寫,原來成員的值就不存在了,而對於struct 的不同成員賦值 是互不影響的。

注:在很多地方需要對結構體的成員變量進行修改。只是部分成員變量,那麼就不能用聯合體Union,因爲Union的所有成員變量佔一個內存。eg:在鏈表中對個別數值域進行賦值就必須用struct.

 

二、實例說明

struct 簡單來說就是一些相互關聯的元素的集合,說是集合,其實它們在內存中的存放是有先後順序的,並且每個元素都有自己的內存空間。那麼按照什麼順序存放的呢?其實就是按你聲明的變量順序來存放的,下面先看一個例子:

struct sTest

{

int a;  //sizeof(int) = 4

char b;  //sizeof(char) = 1

shot c //sizeof(shot) = 2

}x;

所以在內存中至少佔用 4+1+2 = 7 byte。然而實際中佔用的內存並不是7 byte,這就涉及到了字節對齊方式。

 

union 的不同之處就在於,它所有的元素共享同一內存單元,且分配給union的內存size 由類型最大的元素 size 來確定,如下的內存就爲一個double 類型 size 

union uTest

{

int a;   //sizeof(int) = 4

double b;  //sizeof(double) = 8

char c;  //sizeof(char) = 1

}x;

所以分配的內存 size 就是8 byte

既然是內存共享,理所當然地,它不能同時存放多個成員的值,而只能存放其中的一個值,就是最後賦予它的值,如:

x.a = 3; x.b = 4.5; x.c = ‘A’;

這樣你只看到x.c = ‘A’,而其它已經被覆蓋掉,失去了意義。

eg:  Sample聯合只包含其中某一個成員,要麼是index,要麼是price。

union Sample{

   int index;

   double price; };

若  Sample ss; ss.index=10;// 從今往後只能使用ss.index

若  Sample ss; ss.price=14.25;// 從今往後只能使用ss.price

 在union的使用中,如果給其中某個成員賦值,然後使用另一個成員,是未定義行爲,後果自負。

struct成員是互相獨立的,一個struct包含所有成員。

C/C++ code
struct Example
{
int index;
double price;
};

Example結構包含兩個成員,修改index不會對price產生影響,反之亦然。
union的成員共享內存空間,一個union只包含其中某一個成員。

說到這裏,大家應該已經明白兩者最關鍵的區別了吧,無非就在於內存單元的分配和使用。然而要靈活地使用structunion 還是存在許多小技巧的,比如:元素的相關性不強時,完全是可以使用union,從而節省內存size structunion還可以相互嵌套。

 

三、內存對齊方式

union u
{
 double a;
 int b;
};

union u2
{
 char a[13];
 int b;
};

union u3
{
 char a[13];
 char b;
};

cout<<sizeof(u)<<endl; // 8
cout<<sizeof(u2)<<endl; // 16
cout<<sizeof(u3)<<endl; // 13

  都知道union的大小取決於它所有的成員中,佔用空間最大的一個成員的大小。所以對於u來說,大小就是最大的double類型成員a了,所以sizeof(u)=sizeof(double)=8。但是對於u2和u3,最大的空間都是char[13]類型的數組,爲什麼u3的大小是13,而u2是16呢?關鍵在於u2中的成員int b。由於int類型成員的存在,使u2的對齊方式變成4,也就是說,u2的大小必須在4的對界上,所以佔用的空間變成了16(最接近13的對界)。

  結論:複合數據類型,如union,struct,class的對齊方式爲成員中對齊方式最大的成員的對齊方式。

順便提一下CPU對界問題,32的C++採用8位對界來提高運行速度,所以編譯器會盡量把數據放在它的對界上以提高內存命中率。對界是可以更改的,使用#pragma pack(x)宏可以改變編譯器的對界方式,默認是8C++固有類型的對界取編譯器對界方式與自身大小中較小的一個。例如,指定編譯器按2對界,int類型的大小是4,則int的對界爲2和4中較小的2。在默認的對界方式下,因爲幾乎所有的數據類型都不大於默認的對界方式8(除了long double),所以所有的固有類型的對界方式可以認爲就是類型自身的大小。更改一下上面的程序:

#pragma pack(2)
union u2
{
 char a[13];
 int b;
};

union u3
{
 char a[13];
 char b;
};
#pragma pack(8)

cout<<sizeof(u2)<<endl; // 14 由於手動更改對界方式爲2,所以int的對界也變成了2,u2的對界取成員中最大的對界,也是2了,所以此時sizeof(u2)=14。
cout<<sizeof(u3)<<endl; // 13 ,char的對界爲1

   結論:C++固有類型的對界取編譯器對界方式與自身大小中較小的一個。

 

struct的sizeof問題

  因爲對齊問題使結構體的sizeof變得比較複雜,看下面的例子:(默認對齊方式下)
struct s1
{
 char a;
 double b;
 int c;
 char d;
};

struct s2
{
 char a;
 char b;
 int c;
 double d;
};

cout<<sizeof(s1)<<endl; // 24
cout<<sizeof(s2)<<endl; // 16

 同樣是兩個char類型,一個int類型,一個double類型,但是因爲對界問題,導致他們的大小不同。計算結構體大小可以採用元素擺放法,我舉例子說明一下:首先,CPU判斷結構體的對界,根據上一節的結論,s1和s2的對界都取最大的元素類型,也就是double類型的對界8。然後開始擺放每個元素。

  對於s1,首先把a放到8的對界,假定是0,此時下一個空閒的地址是1,但是下一個元素d是double類型,要放到8的對界上,離1最接近的地址是8了,所以d被放在了8,此時下一個空閒地址變成了16,下一個元素c的對界是4,16可以滿足,所以c放在了16,此時下一個空閒地址變成了20,下一個元素d需要對界1,也正好落在對界上,所以d放在了20,結構體在地址21處結束。由於s1的大小需要是8的倍數,所以21-23的空間被保留,s1的大小變成了24。

  對於s2,首先把a放到8的對界,假定是0,此時下一個空閒地址是1,下一個元素的對界也是1,所以b擺放在1,下一個空閒地址變成了2;下一個元素c的對界是4,所以取離2最近的地址4擺放c,下一個空閒地址變成了8,下一個元素d的對界是8,所以d擺放在8,所有元素擺放完畢,結構體在15處結束,佔用總空間爲16,正好是8的倍數

 

特例:

#include<stdio.h>

union{

int i;

char x[2]; }a;

voidmain()

{

a.x[0]=10;

a.x[1]=1;

printf("%d",a.i);

 }


以下與原文有所改動·········································

在聯合體a中定義了兩種數據類型,sizeof(a)=4;字符數組x以及整形變量i.其中整形變量是32位的,數組大小爲2的字符數組爲8X2=16位,char型佔一個字節。如此一來,編譯器便會爲聯合體a在內存中開闢一個32位的空間,這個空間裏存儲聯合體的數據,但是這個空間只有32位,它既是整形變量的數據,也是字符數組的數據。如果你的程序從字符數組的角度解析這個空間,那麼它就是兩個字符,如果你的程序從整型的角度解析這個空間,那麼它就是一個整數。  
  以你的程序爲例子,現在已經開闢了一個32位的空間,然後我們假定現在空間還沒有被賦值,爲:  
  00000000 00000000  00000000 00000000
  那麼在運行完代碼  
  a.x[0] = 10;  
  a.x[1] = 1;  
  之後,16位的空間變爲:  (系統按低字節序)
  00000101 (低字節)

  00000001 

  00000000 

  00000000  (高字節)
  然後程序運行  
  printf("%d",a.i);  
  就是把聯合體a當成一個整數來解析,而不是字符串數組。那麼這樣一來,程序就把這32位變成了一個完整的整數:  
  (00000001 00000101)二進制 =0x01 0A(十六進制)= (266)十進制 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章