Unit 4 · Lesson 616 minAcademic review pending

One space, several possible types

A union stores any one of its members at a time in the same memory. It saves space but forgets which member is valid, so you must remember for it.

Choose explanation

After this lesson

You should be able to

  • Declare a union and explain how it differs from a structure.
  • Explain why reading a member you did not write is unsafe.
  • Pair a union with a tag field to make it safe.
01

Members share the space

In a structure every member has its own memory, so the size is roughly the sum of the members. In a union all members start at the same address, so the size is that of the largest member and only one member is meaningful at a time.

Writing one member overwrites the others. There is no error and no warning — the bytes are simply reinterpreted.

Writing one member destroys the others
union Value {
    int    i;
    double d;
    char   text[8];
};

union Value v;

v.i = 65;
printf("%d\n", v.i);    /* 65 — fine        */

v.d = 3.5;               /* overwrites v.i   */
printf("%d\n", v.i);    /* garbage, not 65  */
02

The union does not remember

Nothing inside a union records which member was last written. Reading a member you did not write reinterprets whatever bytes are there, which is undefined behaviour in general and produces meaningless values in practice.

So a bare union is rarely used. The standard fix is to keep a tag alongside it that says which member is currently valid.

03

A tagged union is the safe form

Wrap the union in a structure with an enum tag. The tag says which member is live, and every read goes through a switch on the tag. Now the compiler cannot check it for you, but your code can.

This is how a value that may be a number or a piece of text is represented in C. Set the tag and the member together, always, and never one without the other.

The tag tells you which member to trust
typedef enum {IS_INT, IS_TEXT} Kind;

typedef struct {
    Kind kind;
    union {
        int  number;
        char text[20];
    } as;
} Field;

void print_field(const Field *f)
{
    if (f->kind == IS_INT)
        printf("%d\n", f->as.number);
    else
        printf("%s\n", f->as.text);
}

Try it yourself

Model a form field that holds either a roll number or a name, and print three such fields correctly.

Need a hint?

You need the enum tag, otherwise printing is guesswork.

Check the worked solution

Each initialiser sets the tag and the matching member together, which is the discipline that makes this safe. Note the memory saving is real but small here; the reason to use a union is that the value genuinely is one thing or the other, not that you are short of bytes.

#include <stdio.h>
#include <string.h>

typedef enum {IS_INT, IS_TEXT} Kind;

typedef struct {
    Kind kind;
    union {
        int  number;
        char text[20];
    } as;
} Field;

void print_field(const Field *f)
{
    if (f->kind == IS_INT)
        printf("Number: %d\n", f->as.number);
    else
        printf("Text:   %s\n", f->as.text);
}

int main(void)
{
    Field fields[3];

    fields[0].kind = IS_INT;
    fields[0].as.number = 101;

    fields[1].kind = IS_TEXT;
    strcpy(fields[1].as.text, "Kavya");

    fields[2].kind = IS_INT;
    fields[2].as.number = 87;

    for (int i = 0; i < 3; i++)
        print_field(&fields[i]);

    return 0;
}

Quick check

How much memory does a union with an int and a double occupy?

Select an answer to check your thinking.

Why this lesson exists

Syllabus mapping

Union Types

Maps to course outcome CO5.