2009-04-07 5 views
31

私はいくつかの組合の例を探していますが、組合のしくみを理解するのではなく、うまくいけば、組合でどのような種類のハックをするのかを見ています。Cの組合の例

ので(:)もちろんのいくつかの説明を)ハックあなたの組合を共有すること自由に感じ

答えて

34

一つの古典は、単純化した仮想マシンのコアのように、「不明」型の値を表現することです例えば、スタックを実装するなどです。

これは(古い、C11以前の)Cであるため、内部共用体には外側のフィールド名が与えられる必要があります。struct C++では、unionを匿名にすることができます。この名前を選ぶのは難しいことがあります。私は孤立して参照されることはほとんどないので、文脈からは何が起こっているのかは常に分かっているので、一文字にする傾向があります。

整数に値を設定するコードは次のようになります。

Value value_new_integer(int v) 
{ 
    Value v; 
    v.type = INTEGER; 
    v.x.integer = v; 
    return v; 
} 

ここで私は(あなたが割り当てることができるstruct sが直接返され、ほとんどのプリミティブ型の値のように扱われることができるという事実を使用しますstructs)。

+0

私は推測しますこれは役に立つかもしれません! – claf

+1

C11は匿名の共用体を提供するので、 'since this C'の段落はC90とC99に適用されますが、C11には適用されません。同様に、この回答は2009年に書かれているので、C11が何を提供するのか予測しないのはかなり合理的でした。 –

+1

@unewind好奇心から、私はこの質問をしています。上記の関数は_redeclaration error_を提供していませんか? –

9

ここで私が毎日使う小さな一です:これは、OLEオートメーションバリアントの定義です

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; 
}; 

データ・タイプ。あなたが見ることができるように、それは可能なタイプの多くを持っています。意図したクライアントコードの機能に応じて、さまざまな状況で使用できるタイプには多くのルールがあります。すべての言語ですべての型がサポートされているわけではありません。

これらの型は、既定で参照によってパラメータを渡すVBScriptなどの言語によって使用されます。VT_BYREFこれは、(VBのような)コードではなく、C++などのバリアント構造の詳細を気にするコードがある場合、必要に応じてvariantパラメータを注意深く参照解除する必要があることを意味します。

byref型は、関数から値を返すためにも使用されます。また、奇妙に誤った名前のSAFEARRAY型を使用する配列型もサポートされています(C++からの使用が難しい)。

文字列の配列を持っている場合は、それをvbscriptに渡すことはできますが、サイズを印刷する以外は使用できません。実際に値を読み取るには、配列データはVT_BYREF | VT_BSTRである必要があります。

+31

OMG、私の目は出血している! – paxdiablo

+0

説明はどこですか? :-) –

+0

私はそれが自明だと思った:)これは、OLEオートメーションバリアントデータ型の定義です。あなたが見ることができるように、それは可能なタイプの多くを持っています。すべての種類がすべての言語でサポートされているわけではありません。 –

2

私はちょうどStackoverflow答えhereの1つを使用したので、6ビットフィールドで構成された単語を2つの16ビット符号なし整数として扱うことができました。

何年も前(最初のARM Cコンパイラ用)も使用しました。当時の命令はすべて32ビットでしたが、正確な命令に応じてレイアウトが異なりました。だから私は、それぞれが特定の命令タイプに適切なビットフィールドを持つ構造体のセットを含むARM命令を表すための組合を持っていました。

typedef enum { INTEGER, STRING, REAL, POINTER } Type; 

typedef struct 
{ 
    Type type; 
    union { 
    int integer; 
    char *string; 
    float real; 
    void *pointer; 
    } x; 
} Value; 

これはあなたが彼らの正確な型を知らなくても、「値」を処理するコードを書くことができます使用:

+0

ARMのものは素晴らしく見栄えがいいです:) – claf

+0

それは:)(しかし、これらのものの移植性についての注意深い話で)私は間違った順序ですべてのビットを周りに得たことが判明しました... –

3
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; 

ユニオンハックでは、単にオブジェクトのベクトルを作ることができます。これはもっときれいにすることができると確信しています...それは私のために働く - キス

+0

SDLは同様のイベント構造体/共用体を使用します。 – aib

6

ユニオンで "ハック"を避けてください、彼らは移植性の頭痛(エンディアン、整列の問題)を引き起こします。

  • 労働組合の正当な使用あなたはそれがどのタイプを知っているように、好ましくはタグで、同じ場所で異なるデータ型を格納することです。 1800 INFORMATIONの例を参照してください。

  • データ型間の変換にユニオンを使用しないでください。整数から数バイトまで。代わりに、移植性のためにシフトとマスキングを使用してください。

+0

いくつかの共用体(linuxのpthread.hを参照してください)は移植性を助けますか? – claf

+1

私のシステムのpthread.hファイルには共用体はありません(そのうち48個すべて)。 – starblue

+0

glibcのnptlソースを見てください – claf

1
#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; 

これは言葉の部品にアクセスするための簡単な方法です。 (実行したら、プラットフォームのエンディアンの変更も簡単に処理できます)

+1

これは実装定義の動作に依存することに注意してください。それは実装があなたが意図するセマンティクスを持つことを定義するプラットフォームにのみ移植可能です。プラットフォームが、例えば、それが文書化されている限り、組合員の記憶域と重複しないことは合法です。 – RBerteig

+1

これはうまくいかないと私は理解していません。 Windows、Linux、ARM、MacOS、およびPPCで動作します。かつて私はそれが失敗するのを見た。とにかく、あなたがSUを望んでいなければ、これは役に立つかもしれないと思った。 – Alphaneo

+2

-1リトルエンディアンマシン(x86など)ではこれが間違っています。機械語とバイトの間の変換には共用体を使わないでください。移植性が悪いです。 – starblue

8

共用体は、コンパイラやインタープリタなどの言語プロセッサの字句解析および解析段階でもよく使用されます。私は今編集中です。

union { 
    char c; 
    int i; 
    string *s; 
    double d; 
    Expression *e; 
    ExpressionList *el; 
    fpos_t fp; 
} 

組合は、字句解析器のトークンとパーサの制作と意味値を関連付けるために使用されます。この練習は、文法ジェネレーターでよく使われます。具体的には、yaccのようなものがあります。組合はその値を保持することができますが、その時点では1つだけです。たとえば、入力ファイルのいずれかのポイントで、文字定数(cに格納されている)または整数(iに格納されている)または浮動小数点数(dに格納されています)を読み取ります。文法ジェネレータは、処理されているルールに応じて、どの時点でどの値が格納されているかを判断するための相当な支援を提供します。

3

私たちは、作業時にパックされたメッセージ(C/C++)にユニオンを使用しているため、ユニオンをデータメンバーとして持つ構造を渡し、構造内のidフィールドに基づいて正しいパスにアクセスできます。

も、ファイルのバージョンがあります思ったので、誰かがファイルに構造を書いたまで働いて発見、今私たちは、ファイルで使用される最大のデータに限定されている、誰も....それを変更しない

だから、しばらくメモリ内の作業に役立ち、ディスクやネットワークに盲目的に書き込むのを防ぎます。

関連する問題