Uniões

Uma união (union) é um tipo especial de estrutura (struct) no qual todos os campos internos iniciam na mesma posição da memória. Em outras palavras, os campos internos são sobrepostos e somente podem armazenar um valor válido de cada vez.

Uniões são uma forma eficiente de armazenar valores de diferentes tipos em uma mesma posição de memória. A quantidade de memória ocupada por uma união corresponde ao tamanho de seu maior campo.

Segue um exemplo de união que permite "enxergar" os bytes individuais de um inteiro sem necessidade de operações de bits ou aritmética de ponteiros:

#include <stdio.h>

typedef union
{
   int value ;                        // inteiro (com largura de N bytes)
   unsigned char byte[sizeof(int)] ;  // vetor de N bytes
} intParts ;

int main ()
{
   intParts a ;
   int i ;

   a.value = 653459 ;
   for (i=0; i < sizeof(int); i++)
      printf ("%02x ", a.byte[i]) ; // imprime um dos bytes 
   printf ("\n") ;

   return 0 ;
}

Uniões podem ser usadas para armazenar valores de diversos tipos em uma única locação de memória. Por exemplo, a união a seguir pode ser usada para armazenar números de diversos tipos:

typedef union
{
  short  shortVal ;
  int    intVal ;
  long   longVal ;
  float  floatVal ;
  double doubleVal ;
} numericValue ;

Como se sabe qual é o tipo do último valor armazenado? Posto de outra forma, qual o valor corrente armazenado na variável da união?

Se armazenarmos um int e tentarmos ler um float, obteremos um valor "errado", pois os valores são lidos byte a byte diretamente da área de memória da união, sem conversões.

Note que errado está entre aspas. Se estamos interessados na representação em binário para um dado tipo, então o que lemos é exatamente a representação binária armazenada em memória. Possivelmente, a versão em binário exige esforço para que seja é inteligível.

#include <stdio.h>

typedef union
{
  short  shortVal ;
  int    intVal ;
  long   longVal ;
  float  floatVal ;
  double doubleVal ;
} numericValue ;

int main ()
{
  numericValue a ;

  a.shortVal = 741 ;
  printf ("short : %d\n\n", a.shortVal) ;

  a.floatVal = 327.5432 ;
  printf ("float : %f\n", a.floatVal) ;
  printf ("short : %d\n\n", a.shortVal) ;

  a.doubleVal = 327.5432 ;
  printf ("double: %lf\n", a.doubleVal) ;
  printf ("float : %f\n", a.floatVal) ;
  printf ("short : %d\n", a.shortVal) ;
  return 0 ;
}

Para resolver esse problema pode ser usado um ''struct'' contendo a união e uma variável que indique o tipo do último valor armazenado:

typedef struct
{
  char type ;
  union
  {
    short  shortVal ;
    int    intVal ;
    long   longVal ;
    float  floatVal ;
    double doubleVal ;
  } ;                    // anonymous union
} numericValue ;

numericValue a ;

a.intVal = 345 ;
a.type = 'i' ;

...

a.doubleVal = 3.141592653589793 ;
a.type = 'd' ;

O exemplo acima mostra uma união anônima, sem nome. Nesse caso, os membros da união são considerados como membros do ''struct'' externo que contém a união. ''structs'' também podem ser anônimos.

Vejamos outro uso interessante de união, no qual as moedas podem ser acessadas com nomes individuais ou como elementos de um vetor:

typedef union {
  struct
  {
    int quarter;
    int dime;
    int nickel;
    int penny;
  };
  int coins[4];
} Coins ;

Coins a ;

// equivalent operations!
a.dime = 34 ;
a.coins[1] = 34 ;

Exercícios

  1. Utilizando uma única variável ''union'', escreva uma função que receba um inteiro e calcule seu quadrado, em seguida, receba um caractere e, caso maiúsculo, imprima minúsculo, caso minúsculo, imprima maiúsculo, e por último, receba uma string de no máximo 8 caracteres e imprima seu inverso.

  2. Variáveis de 32 bits do tipo ''int'' podem representar valores entre −2,147,483,647 e +2,147,483,647, enquanto variáveis de 32 bits do tipo ''unsigned int'' podem representar valores entre 0 e +4,294,967,295. Escreva um programa que receba um valor negativo do tipo ''int'' e mostre qual o valor resultante da conversão para o tipo ''unsigned int''.

  3. Utilizando unions, escreva um programa que, ao receber um determinado valor, calcula o módulo de 256 (%256) desse valor (dica: utilize ''char[sizeof(int)]'').