聯合提供了一種方式,能夠規避C的類型系統,允許以多種類型來引用一個對象。聯合聲明的語法和結構體的語法一樣,只不過語義相差很大。它們不是用不同的域來引用不同的存儲器塊,而是引用同一塊存儲塊。
下面我們來舉幾個例子:
struct STest
{
char c;
int i[ 2 ];
double var;
};
union UTest
{
char c;
int i[ 2 ];
double var;
};
我們可以查看內存裏面的分佈:
類型 c i var 大小
STest 0 4 12 20
UTest 0 0 0 8
上面的數據表示距離首地址的存儲器塊偏移。假如我們定義了UTest* pU; 我們分別察看p->c; p->i[ 0 ]; p->var; 它們所引用的都是數據結構的起始位置。當然求sizeof的話。UTest的大小將是它的最大類型的數據成員的大小。
聯合的用處很多,這裏舉一個怎麼用它來節省空間:
假設我們有一個二叉樹的數據結構,每個葉子節點都有一個double的數據值,而每個內部節點都有指向孩子節點的指針,但是沒有數據(因爲是葉子節點)。如果我們像這樣聲明:
struct NODE
{
struct NODE* pLeft;
struct NODE* pRight;
double data;
};
我們可以知道這樣一個結構體需要16個字節,每個葉子節點都會浪費一半的字節。相反,如果我們用聯合來聲明一個節點:
union NODE
{
struct
{
union NODE* pLeft;
union NODE* pRight;
}inter;
double data;
};
這樣一來,每個節點就只需要8個字節。如果pNode是一個指向union NODE類型的指針,我們用pNode->data來引用葉子節點的數據。而pNode->inter.pLeft和pNode->inter.pRight來引用內部節點的孩子。
這樣可能出現一種情況,就是無法確定是哪種節點(內部節點或葉子節點)。我們可以引用一個標誌域。
struct NODE
{
BOOL isLeaf;
union
{
struct
{
union NODE* pLeft;
union NODE* pRight;
}inter;
double data;
}info;
}
不過對於這樣小的節省而導致代碼的可讀性變得差了一些。在這裏聯合帶來的好處可以忽略。對於較多域的數據結構,這樣的節省會更加吸引人一些。
還有一個用法就是用來訪問不同數據類型的位。如:
UINT floatToBits( float fVar )
{
union
{
float fV;
UINT uI;
}temp;
temp.fV = fVar;
return temp.uI;
}
我們看看彙編代碼:
mov eax,dword ptr [ fVar ]
mov dword ptr [ temp ],eax
它跟下面的函數產生回彙編代碼是一樣的:
UINT floatToBits( UINT var )
{
return var;
}
這就證明彙編代碼裏面缺乏信息,無論是什麼類型都相對於EBP偏移固定的值。過程只是簡單的拷貝,並沒有修改任何位。
再舉個例子吧:
double bitToDouble( UINT uParam1, UINT uParam2 )
{
union
{
double d;
UINT u[ 2 ];
}temp;
temp.u[ 0 ] = uParam1;
temp.u[ 1 ] = uParam2;
return temp.d;
}