雖然平時在程序開發(fā)時較少使用union,雖然當初學C語言時,union一章被老師略過沒有介紹,雖然,自認為自己對union的認識已經(jīng)足夠,但是,在寫完上一篇文章<(大衛(wèi)的閱讀筆記)C++中使用union的幾點思考>之后,網(wǎng)上的討論驅(qū)使我對這一基本的語言特性又投入了一些精力去關(guān)注,并寫成了此文.
下面以MSDN中關(guān)于union的概述作為開頭,這好像有些無聊,不過,有時候看specification的東西可以給我們很多提示,當我們從應(yīng)用的角度去關(guān)注一樣東西的時候,很多更深層次的考慮被我們忽略了.所以,看下去,里面有些東西可能正是你忽略了的.
union
union [tag] { member-list } [declarators];
[union] tag declarators;
The union keyword declares a union type and/or a variable of a union type.
A union is a user-defined data type that can hold values of different types at different times. It is similar to a structure
except that all of its members start at the same location in memory. A union variable can contain only one of its members at
a time. The size of the union is at least the size of the largest member(大衛(wèi)注:我想不出來大于的情況).
For related information, see class, struct, and Anonymous Union.
Declaring a Union
Begin the declaration of a union with the union keyword, and enclose the member list in curly braces:
union UNKNOWN // Declare union type
{
char ch;
int i;
long l;
float f;
double d;
} var1; // Optional declaration of union variable
Using a Union
A C++ union is a limited form of the class type. It can contain access specifiers (public, protected, private), member data,
and member functions, including constructors and destructors. It cannot contain virtual functions or static data members. It
cannot be used as a base class, nor can it have base classes. Default access of members in a union is public.
A C union type can contain only data members.
In C, you must use the union keyword to declare a union variable. In C++, the union keyword is unnecessary:
Example 1
union UNKNOWN var2; // C declaration of a union variable
UNKNOWN var3; // C++ declaration of a union variable
Example 2
A variable of a union type can hold one value of any type declared in the union. Use the member-selection operator (.) to access a member of a union:
var1.i = 6; // Use variable as integer
var2.d = 5.327; // Use variable as double
為了避免對上述文字有稍許的歪曲,我故意沒有翻譯它,但在此對其進行一些歸納:
1.union是一種特殊的struct/class,是一種可用于容納多種類型的類型,但與struct/class不同的是,所有的成員變量共享同一存儲空間(最大的那一個成員類型的大小),這使得它具有多變的特性,可以在不同成員中任意切換,而無需借助強制類型轉(zhuǎn)換,但這也使得你不能把它當作一個成員變量進行修改而不影響到另一成員變量;
2.union也可以有構(gòu)造/析構(gòu)函數(shù),也可以包含訪問標識符,但不能包含虛函數(shù)或靜態(tài)成員變量/方法.
關(guān)于使用union時需要注意的一些問題,可以參考我的前一篇文章:<(大衛(wèi)的閱讀筆記)C++中使用union的幾點思考>.
下面談?wù)勔恍┍容^有意思并且有意義的union的應(yīng)用.
1.in_addr
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
};
對于上面的struct,寫過socket應(yīng)用的人,肯定都用過它.不知你注意過沒有,它包含了一個很有趣的union,該union的各成員具有相同的大小,分別代表同一信息的不同表現(xiàn)形式.你在進行程序設(shè)計的時候也可以利用這一特性來提供同一信息的不同表現(xiàn)形式,不過要注意,在進行跨平臺應(yīng)用時,字節(jié)順序的影響可能給你造成一些不必要的麻煩.
2.匿名union
匿名union是沒有名稱和聲明列表的union,這跟'__unnamed' union不是一回事,它的聲明形式如下:
union { member-list } ;
匿名union僅僅通知編譯器它的成員變量共享一個地址,而變量本身是直接引用的,不使用通常的點號運算符語法.也正因此,匿名union與同一程序塊內(nèi)的其它變量具有相同的作用域級別,需注意命名沖突.
請看下面的例子:
#include <iostream.h>
struct DataForm
{
enum DataType { CharData = 1, IntData, StringData };
DataType type;
// Declare an anonymous union.
union
{
char chCharMem;
char *szStrMem;
int iIntMem;
};
void print();
};
void DataForm::print()
{
// Based on the type of the data, print the
// appropriate data type.
switch( type )
{
case CharData:
cout << chCharMem;
break;
case IntData:
cout << szStrMem;
break;
case StringData:
cout << iIntMem;
break;
}
}
此外,匿名union還具有以下約束:
1).因為匿名聯(lián)合不使用點運算符,所以包含在匿名聯(lián)合內(nèi)的元素必須是數(shù)據(jù),不允許有成員函數(shù),也不能包含私有或受保護的成員;
2).全局匿名聯(lián)合必須是靜態(tài)(static)的,否則就必須放在匿名名字空間中.
附注:
對匿名union的概念,你或許有些陌生,但對于Windows應(yīng)用的開發(fā)人員,有一個經(jīng)常用到的結(jié)構(gòu)中就包含了匿名union,它就是VARIANT,也許你沒有注意它罷了:
typedef struct FARSTRUCT tagVARIANT VARIANT;
typedef struct FARSTRUCT tagVARIANT VARIANTARG;
typedef struct tagVARIANT {
VARTYPE vt;
unsigned short wReserved1;
unsigned short wReserved2;
unsigned short wReserved3;
union {
Byte bVal; // VT_UI1.
Short iVal; // VT_I2.
long lVal; // VT_I4.
float fltVal; // VT_R4.
// ...
};
};
3.利用union進行類型轉(zhuǎn)換
前面已經(jīng)說過,union具有多變的特性,可以在不同成員中任意切換,而無需借助強制類型轉(zhuǎn)換,下面舉例說明這一點(其實1已經(jīng)很好地說明了這一點):
#include <iostream>
using namespace std;
struct DATA
{
char c1;
char c2;
};
int main()
{
union {
int i;
DATA data;
} _ut;
_ut.i = 0x6162;
cout << "_ut.data.c1 = " << _ut.data.c1 << endl
<< "_ut.data.c2 = " << _ut.data.c2 << endl;
return 0;
}
需要提醒你的是,數(shù)據(jù)類型的轉(zhuǎn)換,并非union的專長,只是一個可資利用的特性而已.因為,采用union進行類型間轉(zhuǎn)換極易受平臺影響,如上面的程序采用Intel x86 + Windows 2000 + VC6時輸出為:
_ut.data.c1 = b
_ut.data.c2 = a
(注:因為Intel CPU的架構(gòu)是Little Endian)
而在Sun的Sparc上,你得到的結(jié)果卻是:
_ut.data.c1 =
_ut.data.c2 =
(注:因為采用Big Endian時,前兩個字節(jié)為0x0000)
而即便是在同一平臺上,在integer類型與real類型間進行轉(zhuǎn)換時也不要采用union,否則,你會得到令你莫名其妙的結(jié)論(這是由于CPU對real類型的處理方式引起的,該方式在各平臺上有極大區(qū)別,同時,根據(jù)C++ Standard,這種作法會引起"undefined behavior").
關(guān)于利用引用進行類型轉(zhuǎn)換,可參考<引用在強制類型轉(zhuǎn)化中的應(yīng)用>.
# re: 對union的進一步認識與一些深層應(yīng)用
“我想不出來大于的情況”
--- 還有字節(jié)對齊的情況,和struct一樣
匿名union我經(jīng)常使用,但我不知道它叫“匿名union”,而且還可以放在外邊用,我去試試看,謝謝!
2004-11-26 22:07 |
# re: 對union的進一步認識與一些深層應(yīng)用
你的文章寫的很好,總結(jié)得不錯
2005-05-05 13:34 | peak
# re: 對union的進一步認識與一些深層應(yīng)用
union實際上是一個二進制容器,所以當程序中從不同的角度來解釋這個二進制數(shù)據(jù)的時候非常容易受各種實現(xiàn)細節(jié)的影響,尤其是涉及到這些二進制數(shù)據(jù)在不同平臺和編譯器之間交換的時候。
2005-10-29 00:51 |
# re: 對union的進一步認識與一些深層應(yīng)用
多謝文章。請教問題:union中的成員類型只能是基本的類型,不能使自己定義的類結(jié)構(gòu)么? 比如說 CMyLine m_Ln?
2007-12-13 11:52 | cc
# re: 對union的進一步認識與一些深層應(yīng)用
union既然可以包含struct,自然也是可以包含從class的,但是存在諸多限制,同時,很難想象你的應(yīng)用需要在union中包含一個復雜class,如CString或STL的string。
2008-03-03 15:44 |