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.