ユニオンの仕組みを理解するためではなく、できればうまくいくと思いますが、ユニオンで人々がどのようなハッキングを行うかを確認するために、ユニオンの例を探しています。
だから、あなたのユニオンハックを自由に共有してください(もちろん説明があります:))
古典的な方法の1つは、単純化された仮想マシンのコアのように、「不明」タイプの値を表すことです。
typedef enum { INTEGER, STRING, REAL, POINTER } Type;
typedef struct
{
Type type;
union {
int integer;
char *string;
float real;
void *pointer;
} x;
} Value;
これを使用すると、正確な型を知らなくても「値」を処理するコードを書くことができます。たとえば、スタックの実装などです。
これは(古い、C11以前の)Cにあるため、内部のユニオンには外部のstruct
のフィールド名を指定する必要があります。 C++では、union
を匿名にすることができます。この名前を選ぶのは難しい場合があります。単独で参照されることはほとんどないので、何が起こっているのかがコンテキストから常に明確であるため、私は単一文字で何かをする傾向があります。
値を整数に設定するコードは次のようになります。
Value value_new_integer(int v)
{
Value v;
v.type = INTEGER;
v.x.integer = v;
return v;
}
ここでは、struct
sを直接返すことができ、プリミティブ型の値のように扱うことができるという事実を使用します(struct
sを割り当てることができます)。
これが私が毎日使っている小さなものです。
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
型である必要があります。
ユニオンは、コンパイラやインタプリタなどの言語プロセッサの字句解析および解析段階でも一般的に使用されます。これが私が今編集しているものです。
union {
char c;
int i;
string *s;
double d;
Expression *e;
ExpressionList *el;
fpos_t fp;
}
ユニオンは、セマンティック値を字句アナライザーのトークンおよびパーサーの生成に関連付けるために使用されます。この方法は、yaccのような文法ジェネレータで非常に一般的であり、明示的にサポートします。ユニオンはその値のいずれかを保持できますが、一度に1つの値しか保持できません。たとえば、入力ファイルの任意の時点で、文字定数(c
に格納されている)、整数(i
に格納されている)、または浮動小数点数(格納されている) d
)。グラマージェネレーターは、処理中のルールに応じて、一度にどの値を保存するかを決定するのに非常に役立ちます。
ユニオンとの「ハッキング」を避けてください。これらは移植性の問題(エンディアン、アライメントの問題)を引き起こします。
ユニオンの合法的な使用法は、同じ場所に異なるデータ型を格納することです。できればタグを使用して、どの型であるかを把握してください。 1800情報による例を参照してください。
データ型間の変換にユニオンを使用しないでください。整数から数バイトまで。移植性のために、代わりにシフトとマスキングを使用してください。
作業中のパックされたメッセージ(C/C++)にはユニオンを使用するため、ユニオンをデータメンバーとして構造体をやり取りし、構造体のidフィールドに基づいて正しいパスにアクセスできます。
誰かが構造をファイルに書き込むまではうまくいきましたが、ファイルバージョンが存在しても、誰もそれを変更したことがないため、ファイルで使用される最大データに制限されています。
したがって、メモリ内の作業には役立ちますが、ディスクやネットワークに盲目的に書き込むことは避けてください。
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
偶然にも、Stackoverflowの回答で1つだけを使用しました here ですので、6ビットフィールドで構成されたWordを2つの16ビット符号なし整数として扱うことができました。
何年も前、私は(最初の)ARM Cコンパイラ-当時の命令はすべて32ビットでしたが、正確な命令に応じて異なるレイアウトを使用していました。 ARM命令を表します。各命令には、特定の命令タイプに適切なビットフィールドがあります。
#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;
これは、単語部分にアクセスする簡単な方法です。 (完了したら、プラットフォームのエンディアンネスの変更も簡単に処理できます)