¿Por qué necesitamos sindicatos C?

Resuelto asked hace 16 años • 0 respuestas

¿Cuándo se deben utilizar los sindicatos? ¿Por qué los necesitamos?

 avatar Oct 31 '08 10:10
Aceptado

Las uniones se utilizan a menudo para convertir entre representaciones binarias de números enteros y flotantes:

union
{
  int i;
  float f;
} u;

// Convert floating-point bits to integer:
u.f = 3.14159f;
printf("As integer: %08x\n", u.i);

Aunque este es un comportamiento técnicamente indefinido según el estándar C (se supone que sólo debes leer el campo que se escribió más recientemente), actuará de una manera bien definida en prácticamente cualquier compilador.

Las uniones también se utilizan a veces para implementar pseudopolimorfismo en C, dándole a una estructura alguna etiqueta que indique qué tipo de objeto contiene y luego uniendo los tipos posibles:

enum Type { INTS, FLOATS, DOUBLE };
struct S
{
  Type s_type;
  union
  {
    int s_ints[2];
    float s_floats[2];
    double s_double;
  };
};

void do_something(struct S *s)
{
  switch(s->s_type)
  {
    case INTS:  // do something with s->s_ints
      break;

    case FLOATS:  // do something with s->s_floats
      break;

    case DOUBLE:  // do something with s->s_double
      break;
  }
}

Esto permite que el tamaño de struct Ssea sólo de 12 bytes, en lugar de 28.

Adam Rosenfield avatar Oct 31 '2008 04:10 Adam Rosenfield

Las uniones son particularmente útiles en programación integrada o en situaciones donde se necesita acceso directo al hardware/memoria. Aquí hay un ejemplo trivial:

typedef union
{
    struct {
        unsigned char byte1;
        unsigned char byte2;
        unsigned char byte3;
        unsigned char byte4;
    } bytes;
    unsigned int dword;
} HW_Register;
HW_Register reg;

Luego puede acceder al registro de la siguiente manera:

reg.dword = 0x12345678;
reg.bytes.byte3 = 4;

Por supuesto, el endianismo (orden de bytes) y la arquitectura del procesador son importantes.

Otra característica útil es el modificador de bits:

typedef union
{
    struct {
        unsigned char b1:1;
        unsigned char b2:1;
        unsigned char b3:1;
        unsigned char b4:1;
        unsigned char reserved:4;
    } bits;
    unsigned char byte;
} HW_RegisterB;
HW_RegisterB reg;

Con este código se puede acceder directamente a un único bit de la dirección de registro/memoria:

x = reg.bits.b2;
kgiannakakis avatar Oct 31 '2008 07:10 kgiannakakis

La programación de sistemas de bajo nivel es un ejemplo razonable.

IIRC, he usado uniones para dividir los registros de hardware en los bits de los componentes. Entonces, puede acceder a un registro de 8 bits (como estaba el día que hice esto ;-) en los bits del componente.

(Olvidé la sintaxis exacta pero...) Esta estructura permitiría acceder a un registro de control como un byte de control o mediante bits individuales. Sería importante garantizar que los bits se correspondan con los bits de registro correctos para una endianidad determinada.

typedef union {
    unsigned char control_byte;
    struct {
        unsigned int nibble  : 4;
        unsigned int nmi     : 1;
        unsigned int enabled : 1;
        unsigned int fired   : 1;
        unsigned int control : 1;
    };
} ControlRegister;
Snips avatar Aug 29 '2011 09:08 Snips

Lo he visto en un par de bibliotecas como reemplazo de la herencia orientada a objetos.

P.ej

        Connection
     /       |       \
  Network   USB     VirtualConnection

Si desea que la "clase" de Conexión sea una de las anteriores, puede escribir algo como:

struct Connection
{
    int type;
    union
    {
        struct Network network;
        struct USB usb;
        struct Virtual virtual;
    }
};

Ejemplo de uso en libinfinity: http://git.0x539.de/?p=infinote.git;a=blob;f=libinfinity/common/inf-session.c;h=3e887f0d63bd754c6b5ec232948027cbbf4d61fc;hb=HEAD#l74

bb-generation avatar Aug 29 '2011 09:08 bb-generation