2009-04-07 12 views
31

Sto cercando alcuni esempi di sindacati, non per capire come funziona l'unione, spero di sì, ma per vedere quale tipo di hacker le persone fanno con l'unione.Esempi di Unione in C

Quindi sentitevi liberi di condividere la vostra unione Hack (con qualche spiegazione ovviamente :))

risposta

34

Un classico è quello di rappresentare un valore di tipo "sconosciuto", come nel nucleo di una macchina virtuale semplicistica:

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

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

Usando questo è possibile scrivere codice che gestisce i "valori" senza conoscere il loro tipo esatto ad esempio, implementa uno stack e così via.

Poiché questo è in (vecchio, pre-C11) C, l'unione interna deve avere un nome di campo nell'esterno struct. In C++ puoi lasciare che l'union sia anonimo. Scegliere questo nome può essere difficile. Tendo ad andare con qualcosa di single-letterato, dato che non viene quasi mai fatto riferimento in isolamento e quindi è sempre chiaro dal contesto cosa sta succedendo.

codice per impostare un valore per un intero potrebbe essere simile a questo:

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

Qui uso il fatto che struct s possono essere restituiti direttamente, e trattati quasi come valori di un tipo primitivo (è possibile assegnare struct s).

+0

Immagino questo potrebbe essere utile! – claf

+1

Nota che C11 fornisce unioni anonime, quindi il paragrafo "poiché questo è C" si applica a C90 e C99, ma non a C11.Altrettanto chiaramente, dal momento che questa risposta è stata scritta nel 2009, era del tutto ragionevole non prevedere cosa avrebbe fornito C11. –

+1

@unwind per curiosità, sto facendo questa domanda. La funzione sopra non fornisce _redeclaration error_? –

9

Ecco un piccolo quello che uso tutti i giorni:

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

Questa è la definizione della variante di automazione OLE tipo di dati. Come puoi vedere, ha molti tipi possibili. Esistono molte regole sui tipi che è possibile utilizzare in diverse situazioni, a seconda delle funzionalità del codice client desiderato. Non tutti i tipi sono supportati da tutte le lingue.

I tipi con VT_BYREF dopo di loro sono utilizzati da lingue come VBScript che passano parametri per riferimento predefinita. Ciò significa che se si dispone di un codice che si preoccupa dei dettagli della struttura della variante (come C++) che vengono chiamati dal codice che non lo fa (come VB), allora è necessario devolvere con attenzione il parametro variant, se necessario.

I tipi di byte vengono anche utilizzati per restituire valori dalle funzioni. C'è anche il supporto per i tipi di matrice utilizzando la stranamente misnamed SAFEARRAY tipo - così difficile da usare da C++.

Se si dispone di un array di stringhe, è possibile passarlo a vbscript, ma non può essere utilizzato (tranne che per stampare le dimensioni). Per leggere effettivamente i valori, i dati dell'array devono essere di tipo VT_BYREF | VT_BSTR.

+31

OMG, i miei occhi sanguinano! – paxdiablo

+0

Dov'è la spiegazione? :-) –

+0

Ho pensato che fosse autoesplicativo :) Questa è la definizione del tipo di dati della variante di automazione OLE. Come puoi vedere, ha molti tipi possibili. Non tutti i tipi sono supportati da tutte le lingue –

2

Per coincidenza, ne ho usato uno solo in una risposta Stackoverflow here in modo da poter trattare una parola composta da campi a 6 bit come due interi senza segno a 16 bit.

Anni fa, ne ho anche usato uno per (il primo) compilatore ARM C - le istruzioni in quei giorni erano tutte a 32 bit, ma avevano layout diversi a seconda delle istruzioni esatte. Così ho avuto un sindacato per rappresentare un'istruzione ARM, contenente una serie di struct che ognuno aveva i campi di bit appropriate per un determinato tipo di istruzione.

+0

La cosa ARM sembra carina e brutta :) – claf

+0

Era :) Ma (in un ammonimento sulla portabilità di queste cose) si è scoperto che ho ottenuto tutti i bit nell'ordine sbagliato la prima volta ... –

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; 

con l'unione hack Posso semplicemente creare un vettore di oggetti. Sono sicuro che questo potrebbe essere reso più pulito ...ma funziona per me - KISS

+0

SDL utilizza una struttura/unione eventi simile. – aib

6

Si prega di evitare "hack" con unione, causano mal di testa portabilità (endianness, problemi di allineamento).

  • un uso legittimo del sindacato è quello di memorizzare diversi tipi di dati nello stesso luogo, preferibilmente con un tag in modo da sapere quale tipo si tratta. Vedere l'esempio per 1800 INFORMAZIONI.

  • Non utilizzare l'unione per convertire tra tipi di dati, ad es. da un numero intero a diversi byte. Usa Maiusc e Mascheratura invece per la portabilità.

+0

Un po 'di unione (vedi pthread.h su linux) aiuta la portabilità non la pensi così? – claf

+1

Non c'è unione nei file pthread.h sul mio sistema (tutti e 48 di essi). – starblue

+0

guarda il sorgente nptl 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; 

Questo è un modo semplice per accedere alle parti parole. (Una volta che hai finito, qualsiasi cambiamento di endianness della piattaforma può anche essere gestito facilmente)

+1

Si noti che questo dipende dal comportamento definito dall'implementazione. È solo portabile su piattaforme in cui l'implementazione definisce che avrà la semantica che intendi. Ad esempio, è legale che una piattaforma non si sovrapponga all'archiviazione dei membri di un sindacato, purché l'abbia documentata. – RBerteig

+1

Non capisco quando dici che questo non funzionerà. Funziona su Windows, Linux, ARM, MacOS e PPC. Non una volta l'ho visto fallire. Ad ogni modo, ho pensato che potesse essere utile, se non volessi allora SU. – Alphaneo

+2

-1 Questo errore per le macchine little endian (ad esempio x86). Si prega di non utilizzare l'unione per la conversione tra parole macchina e byte, è un errore per la portabilità. – starblue

8

Le unioni sono anche comunemente utilizzate nell'analisi lessicale e nella fase di analisi dei processori del linguaggio, come compilatori e interpreti. Ecco uno che sto modificando in questo momento.

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

L'unione viene utilizzato per associare i valori semantici con i segni della dell'analizzatore lessicale e le produzioni del parser. Questa pratica è abbastanza comune nei generatori di grammatica, come yacc, che fornisce un supporto esplicito per questo. L'unione può contenere uno qualsiasi dei suoi valori, ma solo uno di essi in quel momento. Ad esempio, in qualsiasi punto del file di input hai letto una costante di carattere (memorizzata in c) o un numero intero (memorizzato in i) o un numero in virgola mobile (memorizzato in d). Il generatore di grammatica fornisce un'assistenza considerevole per determinare quale dei valori è memorizzato in qualsiasi momento a seconda della regola che viene elaborata.

3

Utilizziamo i sindacati per i messaggi compressi al lavoro (C/C++), in modo che possiamo passare attorno a una struttura con un'unione come membro dati, quindi accedere al percorso corretto in base al campo id nella struttura.

ritrovamento Ha lavorato fino a quando qualcuno ha scritto la struttura in un file, ora ci si limita alle più grandi dati utilizzati nel file, perché anche il pensiero c'è una versione del file, nessuno ha mai cambiato ....

Così, mentre utile per il lavoro in memoria, evitare di scriverli ciecamente su disco o rete.