ユニオンがどのように機能するかを理解するためではなく、できれば理解したいのですが、ユニオンでどのようなハックが行われるかを確認するために、ユニオンの例を探しています。
だから、あなたのユニオンハックを自由に共有してください(もちろん、いくつかの説明があります:))
1 つの古典的な方法は、単純な仮想マシンのコアのように、「不明な」型の値を表すことです。
typedef enum { INTEGER, STRING, REAL, POINTER } Type;
typedef struct
{
Type type;
union {
int integer;
char *string;
float real;
void *pointer;
} x;
} Value;
これを使用すると、正確な型を知らなくても「値」を処理するコードを記述できます。たとえば、スタックを実装するなどです。
これは (old, pre-C11) C にあるため、内側の共用体には、外側のフィールド名を指定する必要がありますstruct
。union
C++ では、匿名にすることができます。この名前を選ぶのは難しい場合があります。単独で参照されることはほとんどなく、コンテキストから何が起こっているかが常に明らかであるため、私は一文字で行く傾向があります。
値を整数に設定するコードは次のようになります。
Value value_new_integer(int v)
{
Value v;
v.type = INTEGER;
v.x.integer = v;
return v;
}
ここでは、struct
s を直接返すことができ、プリミティブ型の値のように扱うことができるという事実を利用します ( struct
s を割り当てることができます)。
共用体は、コンパイラーやインタープリターなどの言語プロセッサーの字句解析および構文解析段階でも一般的に使用されます。今編集中のやつです。
union {
char c;
int i;
string *s;
double d;
Expression *e;
ExpressionList *el;
fpos_t fp;
}
ユニオンは、語彙アナライザーのトークンおよびパーサーの生成物に意味値を関連付けるために使用されます。このプラクティスは、明示的なサポートを提供するyaccなどの文法ジェネレーターでは非常に一般的です。共用体はその値のいずれかを保持できますが、一度に保持できる値は 1 つだけです。たとえば、入力ファイルの任意の時点で、文字定数 ( に格納c
)、整数 ( に格納i
)、または浮動小数点数 ( に格納d
) を読み取ります。文法ジェネレーターは、処理されるルールに応じて、一度に格納される値を決定するためにかなりの支援を提供します。
これは私が毎日使っている小さなものです:
struct tagVARIANT {
union {
struct __tagVARIANT {
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union {
LONG lVal; /* VT_I4 */
BYTE bVal; /* VT_UI1 */
SHORT iVal; /* VT_I2 */
FLOAT fltVal; /* VT_R4 */
DOUBLE dblVal; /* VT_R8 */
VARIANT_BOOL boolVal; /* VT_BOOL */
_VARIANT_BOOL bool; /* (obsolete) */
SCODE scode; /* VT_ERROR */
CY cyVal; /* VT_CY */
DATE date; /* VT_DATE */
BSTR bstrVal; /* VT_BSTR */
IUnknown * punkVal; /* VT_UNKNOWN */
IDispatch * pdispVal; /* VT_DISPATCH */
SAFEARRAY * parray; /* VT_ARRAY */
BYTE * pbVal; /* VT_BYREF|VT_UI1 */
SHORT * piVal; /* VT_BYREF|VT_I2 */
LONG * plVal; /* VT_BYREF|VT_I4 */
FLOAT * pfltVal; /* VT_BYREF|VT_R4 */
DOUBLE * pdblVal; /* VT_BYREF|VT_R8 */
VARIANT_BOOL *pboolVal; /* VT_BYREF|VT_BOOL */
SCODE * pscode; /* VT_BYREF|VT_ERROR */
CY * pcyVal; /* VT_BYREF|VT_CY */
DATE * pdate; /* VT_BYREF|VT_DATE */
BSTR * pbstrVal; /* VT_BYREF|VT_BSTR */
IUnknown ** ppunkVal; /* VT_BYREF|VT_UNKNOWN */
IDispatch ** ppdispVal; /* VT_BYREF|VT_DISPATCH */
SAFEARRAY ** pparray; /* VT_BYREF|VT_ARRAY */
VARIANT * pvarVal; /* VT_BYREF|VT_VARIANT */
PVOID byref; /* Generic ByRef */
CHAR cVal; /* VT_I1 */
USHORT uiVal; /* VT_UI2 */
ULONG ulVal; /* VT_UI4 */
INT intVal; /* VT_INT */
UINT uintVal; /* VT_UINT */
DECIMAL * pdecVal; /* VT_BYREF|VT_DECIMAL */
CHAR * pcVal; /* VT_BYREF|VT_I1 */
USHORT * puiVal; /* VT_BYREF|VT_UI2 */
ULONG * pulVal; /* VT_BYREF|VT_UI4 */
INT * pintVal; /* VT_BYREF|VT_INT */
UINT * puintVal; /* VT_BYREF|VT_UINT */
} __VARIANT_NAME_3;
} __VARIANT_NAME_2;
DECIMAL decVal;
} __VARIANT_NAME_1;
};
これは、OLE オートメーション バリアント データ型の定義です。ご覧のとおり、可能なタイプがたくさんあります。目的のクライアント コードの機能に応じて、さまざまな状況で使用できる型に関する多くの規則があります。すべてのタイプがすべての言語でサポートされているわけではありません。
後に続く型はVT_BYREF
、既定で参照によってパラメーターを渡す VBScript などの言語で使用されます。これは、バリアント構造の詳細を気にするコード (C++ など) が、そうでないコード (VB など) によって呼び出される場合、必要に応じてバリアント パラメーターを慎重に逆参照する必要があることを意味します。
byref 型は、関数から値を返すためにも使用されます。奇妙に誤った名前の型を使用する配列型もサポートされていSAFEARRAY
ます - C++ から使用するのは非常に困難です。
文字列の配列がある場合、それを vbscript に渡すことはできますが、使用することはできません (サイズの出力を除く)。実際に値を読み取るには、配列データの型が である必要がありますVT_BYREF | VT_BSTR
。
ユニオンによる「ハック」は避けてください。移植性の問題 (エンディアン、アラインメントの問題) を引き起こします。
ユニオンの正当な使用法は、異なるデータ型を同じ場所に格納することです。できればタグを付けて、それがどの型であるかを知ることができます。1800 INFORMATION の例を参照してください。
整数から数バイトなど、データ型間の変換に共用体を使用しないでください。移植性のために、代わりにシフトとマスキングを使用してください。
作業中のパックされたメッセージ (C/C++) に共用体を使用するため、データ メンバーとして共用体を使用して構造体を渡し、構造体の id フィールドに基づいて正しいパスにアクセスできます。
誰かが構造をファイルに書き込むまでは問題なく機能していましたが、現在はファイルで使用される最大のデータに制限されています。
そのため、メモリ内作業には便利ですが、やみくもにディスクやネットワークに書き込むことは避けてください。
偶然にも、ここで Stackoverflow の回答で 1 つを使用したので、6 ビット フィールドで構成された単語を 2 つの 16 ビット符号なし整数として扱うことができました。
何年も前に、(最初の) ARM C コンパイラにも 1 つ使用しました。当時の命令はすべて 32 ビットでしたが、正確な命令に応じてレイアウトが異なっていました。そのため、ARM 命令を表す共用体があり、それぞれが特定の命令タイプに適切なビットフィールドを持つ一連の構造体を含んでいました。
struct InputEvent
{
enum EventType
{
EventKeyPressed,
EventKeyPressRepeated,
EventKeyReleased,
EventMousePressed,
EventMouseMoved,
EventMouseReleased
} Type;
union
{
unsigned int KeyCode;
struct
{
int x;
int y;
unsigned int ButtonCode;
};
};
};
...
std::vector<InputEvent> InputQueue;
ユニオン ハックを使用すると、オブジェクトのベクトルを簡単に作成できます。これはもっときれいにできると確信しています...しかし、それは私にとってはうまくいきます-KISS
#define DWORD unsigned int
#define WORD unsigned short
#define BYTE unsigned char
typedef union _DWORD_PART_ {
DWORD dwWord;
struct {
WORD dwMSB;
WORD dwLSB;
}hw;
struct {
BYTE byMSB;
BYTE byMSBL;
BYTE byLSBH;
BYTE byLSB;
} b;
} DWORD_PART;
これは単語部分にアクセスする簡単な方法です。(いったん完了したら、プラットフォームのエンディアンの変更も簡単に処理できます)