2009-04-07 17 views
31

Estoy buscando algunos ejemplos de unión, no para entender cómo funciona la unión, con suerte lo hago, sino para ver qué clase de hackeo hacen las personas con la unión.Ejemplos de unión en C

Así que no dude en compartir su cooperativa de Hack (con alguna explicación, por supuesto :))

Respuesta

34

Un clásico es para representar un valor de tipo "desconocido", como en el núcleo de una máquina virtual simplista:

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

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

El uso de este se puede escribir código que se encarga de "valores" sin conocer su tipo exacto , por ejemplo, implementar una pila, etc.

Dado que está en (anterior, anterior a C11) C, la unión interna debe tener un nombre de campo en el exterior struct. En C++ puede dejar que el union sea anónimo. Escoger este nombre puede ser difícil. Tiendo a ir con algo de una sola letra, ya que casi nunca se hace referencia de forma aislada y, por lo tanto, siempre está claro, desde el contexto, lo que está sucediendo.

Código para establecer un valor a un entero podría tener este aspecto:

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

Aquí, he utilizado el hecho de que struct s pueden ser devueltos directamente, y se tratan casi como valores de un tipo primitivo (puede asignar struct s).

+0

supongo ¡este podría ser útil! – claf

+1

Tenga en cuenta que C11 proporciona uniones anónimas, por lo que el párrafo 'ya que es C' se aplica a C90 y C99, pero no a C11.Igualmente claro, ya que esta respuesta se escribió en 2009, era bastante razonable no predecir lo que C11 proporcionaría. –

+1

@unwind por curiosidad, estoy haciendo esta pregunta. ¿La función anterior no proporciona _redeclaration error_? –

9

Aquí hay un poco que yo uso todos los días gratis:

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

Ésta es la definición de la variante de la automatización OLE tipo de datos. Como puede ver, tiene muchos tipos posibles. Existen muchas reglas sobre los tipos que puede usar en diferentes situaciones, dependiendo de las capacidades de su código de cliente deseado. No todos los tipos son compatibles con todos los idiomas.

Los tipos con VT_BYREF después de ellos son utilizados por lenguajes como VBScript que pasan los parámetros por referencia de forma predeterminada. Esto significa que si tiene algún código que se preocupe por los detalles de la estructura variante (como C++) que son invocados por un código que no lo hace (como VB), entonces debe desreferenciar cuidadosamente el parámetro de la variante si es necesario.

Los tipos de byref también se utilizan para devolver valores de funciones. También hay soporte para tipos de matrices con el tipo extrañamente mal llamado SAFEARRAY, tan difícil de usar desde C++.

Si tiene una matriz de cadenas, puede pasarla a vbscript, pero no puede usarse (excepto para imprimir el tamaño). Para leer realmente los valores, los datos de la matriz deben ser del tipo VT_BYREF | VT_BSTR.

+31

¡Dios mío, mis ojos están sangrando! – paxdiablo

+0

¿Dónde está la explicación? :-) –

+0

Pensé que se explicaba por sí mismo :) Esta es la definición del tipo de datos de variante de automatización OLE. Como puede ver, tiene muchos tipos posibles. No todos los tipos son compatibles con todos los idiomas –

2

Casualmente, acabo de utilizar uno en una respuesta Stackoverflow here para poder tratar una palabra compuesta de campos de 6 bits como dos enteros sin signo de 16 bits.

Hace años, también usé uno para (el primer) compilador ARM C - las instrucciones en esos días eran todas de 32 bits, pero tenían diferentes diseños dependiendo de la instrucción exacta. Así que tuve una unión para representar una instrucción ARM, que contenía un conjunto de estructuras que cada una tenía los campos de bits apropiados para un tipo de instrucción específico.

+0

Lo ARM vea bonito y feo :) – claf

+0

Fue :) Pero (en una advertencia acerca de la portabilidad de estas cosas) resultó que me dieron todos los bits en el orden equivocado la primera vez ... –

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 el union hack Puedo simplemente hacer un vector de objetos. Estoy seguro de que esto podría hacerse más limpio ...pero funciona para mí - KISS

+0

SDL utiliza una estructura/unión de eventos similar. – aib

6

Por favor, evita "hacks" con el sindicato, que causan dolores de cabeza de portabilidad (orden de bits, problemas de alineación).

  • Un uso legítimo de la unión es almacenar diferentes tipos de datos en el mismo lugar, de preferencia con una etiqueta para que sepa qué tipo es. Vea el ejemplo por 1800 INFORMACIÓN.

  • No utilice unión para convertir entre tipos de datos, por ejemplo, de un entero a varios bytes. Utilice el cambio y el enmascaramiento en lugar de la portabilidad.

+0

Alguna unión (vea pthread.h en Linux) ayuda a la portabilidad, ¿no cree? – claf

+1

No hay unión en los archivos pthread.h en mi sistema (los 48 de ellos). – starblue

+0

mira la fuente nptl en 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; 

Esta es una manera fácil de acceder a las partes de las palabras. (Una vez que haya terminado, cualquier cambio en la endianidad de la plataforma también se puede manejar fácilmente)

+1

Tenga en cuenta que esto depende del comportamiento definido de implementación. Solo es portátil para plataformas donde la implementación define que tendrá la semántica que usted pretende. Es legal que una plataforma no solape el almacenamiento de los miembros de un sindicato, por ejemplo, siempre que lo documente. – RBerteig

+1

No entiendo cuando dices que esto no funcionará. Funciona en Windows, Linux, ARM, MacOS y PPC. No una vez que lo he visto fracasar. De todos modos, pensé que esto podría ser útil, si no quieres entonces SU. – Alphaneo

+2

-1 Este error para las pequeñas máquinas endian (por ejemplo, x86). No utilice la unión para convertir entre palabras de máquina y bytes, es malo para la portabilidad. – starblue

8

Las uniones también se utilizan comúnmente en la etapa de análisis léxico y análisis de procesadores de lenguaje, como compiladores e intérpretes. Aquí hay uno que estoy editando en este momento.

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

La unión se utiliza para asociar valores semánticos con las señales de los analizador léxico y las producciones del analizador. Esta práctica es bastante común en los generadores de gramática, como yacc, que proporciona soporte explícito para ello. La unión puede contener cualquiera de sus valores, pero solo uno de ellos en ese momento. Por ejemplo, en cualquier punto del archivo de entrada, puede leer una constante de caracteres (almacenada en c) o un número entero (almacenado en i) o un número de coma flotante (almacenado en d). El generador de gramática proporciona una asistencia considerable para determinar cuál de los valores se almacena en cualquier momento dependiendo de la regla que se está procesando.

3

Utilizamos las uniones de mensaje lleno en el trabajo (C/C++), por lo que puede pasar alrededor de una estructura con una unión como un miembro de datos, a continuación, acceder al camino correcto basado en campo id en la estructura.

hallazgo trabajó hasta que alguien escribió la estructura de un archivo, ya que estamos limitados a los datos más importantes utilizados en el archivo, ya que incluso pensaron que hay una versión de archivo, nadie lo cambió ....

Así, mientras útil para el trabajo en memoria, evite escribirlos a ciegas en el disco o la red.