6.3. Unions

Unions don't take long to explain. They are the same as structures, except that, where you would have written struct before, now you write union. Everything works the same way, but with one big exception. In a structure, the members are allocated separate consecutive chunks of storage. In a union, every member is allocated the same piece of storage. What would you use them for? Well, sometimes you want a structure to contain different values of different types at different times but to conserve space as much as possible. Using a union, it's up to you to keep track of whatever type you put into it and make sure that you retrieve the right type at the right time. Here's an example:

#include <stdio.h>
#include <stdlib.h>

main(){
      union {
              float u_f;
              int u_i;
      }var;

      var.u_f = 23.5;
      printf("value is %f\n", var.u_f);
      var.u_i = 5;
      printf("value is %d\n", var.u_i);
      exit(EXIT_SUCCESS);
}
Example 6.11

If the example had, say, put a float into the union and then extracted it as an int, a strange value would have resulted. The two types are almost certainly not only stored differently, but of different lengths. The int retrieved would probably be the low-order bits of the machine representation of a float, and might easily be made up of part of the mantissa of the float plus a piece of the exponent. The Standard says that if you do this, the behaviour is implementation defined (not undefined). The behaviour is defined by the Standard in one case: if some of the members of a union are structures with a ‘common initial sequence’ (the first members of each structure have compatible type and in the case of bitfields are the same length), and the union currently contains one of them, then the common initial part of each can be used interchangeably. Oh good.

The C compiler does no more than work out what the biggest member in a union can be and allocates enough storage (appropriately aligned if neccessary). In particular, no checking is done to make sure that the right sort of use is made of the members. That is your task, and you'll soon find out if you get it wrong. The members of a union all start at the same address—there is guaranteed to be no padding in front of any of them.

The most common way of remembering what is in a union is to embed it in a structure, with another member of the structure used to indicate the type of thing currently in the union. Here is how it might be used:

#include <stdio.h>
#include <stdlib.h>

/* code for types in union */
#define FLOAT_TYPE      1
#define CHAR_TYPE       2
#define INT_TYPE        3

struct var_type{
      int type_in_union;
      union{
              float   un_float;
              char    un_char;
              int     un_int;
      }vt_un;
}var_type;

void
print_vt(void){

      switch(var_type.type_in_union){
              default:
                      printf("Unknown type in union\n");
                      break;
              case FLOAT_TYPE:
                      printf("%f\n", var_type.vt_un.un_float);
                      break;
              case CHAR_TYPE:
                      printf("%c\n", var_type.vt_un.un_char);
                      break;
              case INT_TYPE:
                      printf("%d\n", var_type.vt_un.un_int);
                      break;
      }
}

main(){

      var_type.type_in_union = FLOAT_TYPE;
      var_type.vt_un.un_float = 3.5;

      print_vt();

      var_type.type_in_union = CHAR_TYPE;
      var_type.vt_un.un_char = 'a';

      print_vt();
      exit(EXIT_SUCCESS);
}
Example 6.12

That also demonstrates how the dot notation is used to access structures or unions inside other structures or unions. Some current C compilers allow you to miss bits out of the names of embedded objects provided that they are not ambiguous. In the example, such an unambiguous name would be var_type.un_int and the compiler would work out what you meant. None the less this is not permitted by the Standard.

It is because of unions that structures cannot be compared for equality. The possibility that a structure might contain a union makes it hard to compare such structures; the compiler can't tell what the union currently contains and so wouldn't know how to compare the structures. This sounds a bit hard to swallow and isn't 100% true—most structures don't contain unions—but there is also a philosophical issue at stake about just what is meant by ‘equality’ when applied to structures. Anyhow, the union business gives the Standard a good excuse to avoid the issue by not supporting structure comparison.