2009-04-07 9 views
31

Ich suche nach einigen Gewerkschaftsbeispielen, nicht zu verstehen, wie Gewerkschaft funktioniert, hoffentlich ich, aber um zu sehen, welche Art von Hack Menschen mit Union tun.Beispiele von Union in C

So fühlen sich frei, Ihre Gewerkschaft hacken zu teilen (mit etwas Erklärung natürlich :))

Antwort

34

Ein Klassiker ist ein Wert von „unbekannten“ Typ zu repräsentieren, wie es in den Kern einer stark vereinfachten virtuellen Maschine:

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

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

Mit diesem Sie Code schreiben können, die „Werte“ ohne zu wissen, ihre genaue Griffe zB einen Stack implementieren und so weiter.

Da dies in (alt, vor C11) C ist, muss die innere Union einen Feldnamen im äußeren struct erhalten. In C++ können Sie die union anonym lassen. Diesen Namen auszusuchen kann schwierig sein. Ich neige dazu, mit etwas einzigem Buchstaben zu gehen, da es fast nie isoliert betrachtet wird und somit immer aus dem Kontext klar ist, was vor sich geht.

-Code Wert auf eine ganze Zahl setzen könnte wie folgt aussehen:

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

Hier verwende ich die Tatsache, dass struct s direkt zurückgeführt werden kann, und fast wie Werte einer primitiven Art behandelt (Sie können zuweisen struct s).

+0

Ich denke dieser könnte nützlich sein! – claf

+1

Beachten Sie, dass C11 anonyme Verbindungen bietet. Daher gilt der Absatz "da dies C ist" für C90 und C99, nicht jedoch für C11.Ebenso klar, da diese Antwort im Jahr 2009 verfasst wurde, war es ziemlich vernünftig, nicht vorauszusagen, was C11 bieten würde. –

+1

@unwind aus Neugier, ich frage diese Frage. Bietet die obige Funktion keinen _redeclaration error_? –

9

Hier ist ein kleiner, den ich jeden Tag benutzen:

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

Dies ist die Definition der OLE-Automation-Variante Datentyp. Wie Sie sehen können, hat es viele mögliche Typen. Es gibt viele Regeln für die Typen, die Sie in verschiedenen Situationen verwenden können, abhängig von den Funktionen Ihres beabsichtigten Client-Codes. Nicht alle Typen werden von allen Sprachen unterstützt.

Die Typen mit VT_BYREF nach ihnen werden von Sprachen wie VBScript verwendet, die Parameter standardmäßig per Referenz übergeben. Das bedeutet, wenn Sie Code haben, der sich um die Details der Variantenstruktur kümmert (z. B. C++), die von Code aufgerufen werden, der dies nicht tut (z. B. VB), müssen Sie den Variantenparameter bei Bedarf sorgfältig dereferenzieren.

Die byref-Typen werden auch verwendet, um Werte aus Funktionen zurückzugeben. Es gibt auch Unterstützung für Array-Typen, die den seltsamen Typ SAFEARRAY verwenden - so schwierig von C++ zu verwenden.

Wenn Sie ein Array mit Zeichenfolgen haben, können Sie es an VBScript übergeben, es kann jedoch nicht verwendet werden (außer zum Drucken der Größe). Um die Werte tatsächlich zu lesen, müssen die Array-Daten vom Typ VT_BYREF | VT_BSTR sein.

+31

OMG, meine Augen bluten! – paxdiablo

+0

Wo ist die Erklärung? :-) –

+0

Ich dachte, es wäre selbsterklärend :) Dies ist die Definition des Datentyps OLE-Automatisierungsvariante. Wie Sie sehen können, hat es viele mögliche Typen. Nicht alle Typen werden von allen Sprachen unterstützt –

2

Zufälligerweise habe ich nur eine in einer Stackoverflow Antwort here verwendet, so dass ich ein Wort, das aus 6-Bit-Feldern bestand, als zwei 16-Bit-Ganzzahlen ohne Vorzeichen behandeln konnte.

Vor Jahren benutzte ich auch einen für den (ersten) ARM C Compiler - die Anweisungen waren damals alle 32 Bit, hatten aber je nach Anweisung unterschiedliche Layouts. Also hatte ich eine Union, um eine ARM-Anweisung darzustellen, die eine Menge von Strukturen enthielt, die jeweils die entsprechenden Bitfelder für einen bestimmten Befehlstyp hatten.

+0

Die ARM-Sache sieht gut aus und hässlich :) – claf

+0

Es war :) Aber (in einer warnenden Geschichte über Portabilität dieser Dinge) stellte sich heraus, dass ich alle Bits in der falschen Reihenfolge beim ersten Mal ... –

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; 

mit dem Gewerk hack kann ich einfach einen Vektor von Objekten machen. Ich bin mir sicher, das könnte sauberer gemacht werden ...aber es funktioniert für mich - KISS

+0

SDL verwendet eine ähnliche Ereignisstruktur/-union. – aib

6

Bitte vermeiden Sie "Hacks" mit Union, sie verursachen Portabilität Kopfschmerzen (Endianness, Alignment-Probleme).

  • Eine legitime Nutzung der Vereinigung ist verschiedene Datentypen an der gleichen Stelle zu speichern, vorzugsweise mit einem Tag, so dass Sie wissen, welche Art es ist. Siehe das Beispiel von 1800 INFORMATION.

  • Verwenden Sie keine Union zum Konvertieren zwischen Datentypen, z. von einer ganzen Zahl bis zu mehreren Bytes. Verwenden Sie stattdessen Shift und Maskierung für die Portabilität.

+0

Einige Union (siehe pthread.h auf Linux) hilft Portabilität, denkst du nicht? – claf

+1

Es gibt keine Vereinigung in den pthread.h Dateien auf meinem System (alle 48 von ihnen). – starblue

+0

Blick auf die Nptl Quelle in glibc – 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; 

Dies ist eine einfache Art und Weise die Worte Teile zuzugreifen. (Sobald Sie fertig sind, kann jede Änderung in der Endianz der Plattform auch leicht gehandhabt werden)

+1

Beachten Sie, dass dies vom implementierungsdefinierten Verhalten abhängt. Es ist nur auf Plattformen übertragbar, auf denen die Implementierung definiert, dass es die von Ihnen beabsichtigte Semantik hat. Es ist legal, dass sich eine Plattform beispielsweise nicht so lange mit der Speicherung von Mitgliedern einer Gewerkschaft überschneidet, wie dies dokumentiert ist. – RBerteig

+1

Ich verstehe nicht, wenn Sie sagen, das wird nicht funktionieren. Es funktioniert in Windows, Linux, ARM, MacOS und PPC. Nicht einmal ich habe es gescheitert gesehen. Wie auch immer, ich dachte, das könnte nützlich sein, wenn du nicht willst, SU. – Alphaneo

+2

-1 Dies ist falsch für kleine Endian-Maschinen (z. B. x86). Bitte verwenden Sie keine Union für die Konvertierung zwischen Maschinenwörtern und Bytes, es ist schlecht für die Portabilität. – starblue

8

Gewerkschaften werden auch häufig in der lexikalischen Analyse- und Parsing-Phase von Sprachprozessoren wie Compilern und Interpretern verwendet. Hier ist eine, die ich gerade bearbeite.

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

Die Verbindung wird verwendet, um mit dem Tokens des lexikalischen Analysators und den Produktionen des Parsers semantische Werte zuzuordnen. Diese Praxis ist in Grammatikgeneratoren wie yacc ziemlich üblich, die explizite Unterstützung dafür bietet. Die Gewerkschaft kann irgendeinen ihrer Werte halten, aber nur einen von ihnen zu der Zeit. Zum Beispiel haben Sie an irgendeinem Punkt der Eingabedatei entweder eine Zeichenkonstante (gespeichert in c) oder eine ganze Zahl (gespeichert in i) oder eine Gleitkommazahl (gespeichert in d) gelesen. Der Grammatikgenerator bietet eine beträchtliche Hilfe bei der Bestimmung, welcher der Werte zu einem bestimmten Zeitpunkt in Abhängigkeit von der zu verarbeitenden Regel gespeichert wird.

3

Wir verwenden Unions für gepackte Nachrichten bei der Arbeit (C/C++), so dass wir eine Struktur mit einer Union als Datenelement weitergeben und dann auf den richtigen Pfad basierend auf dem ID-Feld in der Struktur zugreifen können.

Arbeitete finden, bis jemand die Struktur in eine Datei geschrieben, jetzt sind wir zu den größten Daten beschränkt, die in der Datei verwendet, denn auch gedacht, es gibt eine Dateiversion, niemand jemals veränderte es ....

So, während nützlich für In-Memory-Arbeit, vermeiden Sie blind auf Datenträger oder Netzwerk zu schreiben.