Unions & Enums in C – Where and Why to Use Them?

Unions & Enums in C – Where and Why to Use Them? | Tamil Technicians

Tamil Technicians – C Programming Course · Lesson 13

Unions & Enums in C – Where and Why to Use Them?

As you go deeper into C programming, you will sometimes need more expressive and memory-efficient ways to represent data. So far, you have seen:

  • Basic types: int, float, char, etc.
  • Arrays: 1D and 2D for lists and tables.
  • struct: to group different fields into one record.

Two additional tools in C are: union and enum. They are less used in beginner programs, but very important in real systems, especially in embedded programming, device drivers and protocol parsers.

In this lesson you will learn:

  • What a union is and how it shares memory between fields.
  • Real examples where unions save memory or simplify “variant” data.
  • What an enum is and why it’s better than plain integer codes.
  • How to use enums with switch for readable state machines.
  • Best practices and common mistakes to avoid.
Unions and enums in C with memory diagram and mode list – Tamil Technicians
Figure: A union sharing one memory block for multiple views, and an enum listing named modes for a device.

Quick Overview – What Are Unions and Enums?

Union (Shared Memory)

A union is like a struct, but all members share the same memory location. At any point, you are expected to use only one active field.

This is useful when a value can be of different types at different times, and you want to save memory.

Enum (Named Integer Values)

An enum (enumeration) is a way to give names to integer constants.

This makes code more readable and self-explanatory than using raw numbers like 0, 1, 2 for modes or states.

Big picture

Use unions when you need different representations of the same data in the same memory space. Use enums when you need meaningful, named states or categories instead of “magic numbers”.

Enums in C – Named Integer Constants

An enum lets you group related integer constants together under one type name. Syntax:

enum enum_name {
    CONSTANT1,
    CONSTANT2,
    CONSTANT3
};

Example – device modes:

enum DeviceMode {
    MODE_OFF,      // 0
    MODE_IDLE,     // 1
    MODE_RUN,      // 2
    MODE_ERROR     // 3
};

Under the hood, MODE_OFF is just an int with value 0, MODE_IDLE is 1, and so on (by default).

You can also assign custom integer values:
enum DeviceMode {
    MODE_OFF = 0,
    MODE_IDLE = 10,
    MODE_RUN = 20,
    MODE_ERROR = 99
};

Using Enums – Why They Are Better Than Plain Integers

Without enums, you might write:

#define MODE_OFF   0
#define MODE_IDLE  1
#define MODE_RUN   2
#define MODE_ERROR 3

int mode = 2;   // what is 2?

If someone reads mode = 2;, they have to search where 2 is defined. With enums:

enum DeviceMode mode;
mode = MODE_RUN;   // much clearer

Example: using enum with switch:

#include <stdio.h>

enum DeviceMode {
    MODE_OFF,
    MODE_IDLE,
    MODE_RUN,
    MODE_ERROR
};

int main() {
    enum DeviceMode mode = MODE_RUN;

    switch (mode) {
        case MODE_OFF:
            printf("Device is OFF\\n");
            break;
        case MODE_IDLE:
            printf("Device is IDLE\\n");
            break;
        case MODE_RUN:
            printf("Device is RUNNING\\n");
            break;
        case MODE_ERROR:
            printf("Device ERROR\\n");
            break;
    }
    return 0;
}
Benefits of enums
  • Code is more readable (MODE_RUN vs 2).
  • Less chance of typo with random numbers.
  • Easy to add new states later.
  • Great with switch for state machines and menus.

Practical Enum Examples for Technicians

Example 1 – Error Codes

enum ErrorCode {
    ERR_OK = 0,
    ERR_SENSOR_FAIL = 1,
    ERR_OVER_VOLTAGE = 2,
    ERR_OVER_CURRENT = 3,
    ERR_COMMUNICATION = 4
};

Using this, you can return meaningful error codes from functions:

enum ErrorCode readSensor() {
    // ...
    return ERR_SENSOR_FAIL;
}

Example 2 – Menu Choices

enum MenuChoice {
    MENU_NONE = 0,
    MENU_START = 1,
    MENU_STOP = 2,
    MENU_STATUS = 3,
    MENU_EXIT = 9
};

Now your switch on user input will be much clearer when you use constants like MENU_EXIT instead of just 9.

Unions in C – Shared Memory for Different Views

A union is similar to a struct, but with one key difference:

Definition

In a struct, each member has its own memory. In a union, all members share the same memory location. The size of a union is equal to the size of its largest member.

Basic Syntax

union Data {
    int   i;
    float f;
    char  c;
};

Here, Data can hold either an int, or a float, or a char – but not all three meaningfully at the same time.

union Data d;
d.i = 42;   // store int
// later
d.f = 3.14f; // now interpreted as float, overwrites previous int

The memory is shared, so the last write decides which type of data is stored.

Union vs Struct – Memory Diagram

Feature struct union
Memory layout Each member has its own space; total size ≥ sum of members. All members share the same space; size = largest member.
Usage Store many related values at once. Store one of several possible representations at a time.
Example Student record: name, age, marks, etc. Packet payload that can be in different formats.

Struct Example

struct Point {
    int x;
    int y;
};

This always stores both x and y.

Union Example

union Number {
    int   i;
    float f;
};

Here the same memory is viewed as either an int or a float. You choose which interpretation is valid at any time.

Where and Why to Use Unions?

Unions are most useful in these situations:

  • Memory-constrained systems – Embedded devices, microcontrollers, where RAM is very limited.
  • Variant data – When a value can be in different formats at different times (like multiple sensor formats, different packet types).
  • Reinterpreting data – Viewing the same bytes as different types (for example, breaking a 32-bit integer into 4 bytes).

Example 1 – Sensor Value with Multiple Units

Suppose your system can represent a measurement as integer milli-units or as float units, but at runtime you only need one representation.

union SensorValue {
    int   milli;   // value in milli-units
    float real;    // value as float
};

enum ValueType {
    VAL_MILLI,
    VAL_REAL
};

struct Reading {
    enum ValueType type;
    union SensorValue value;
};

Here, Reading is a classic example of a tagged union: the type field (enum) tells you how to interpret the union value.

struct Reading r;
r.type = VAL_MILLI;
r.value.milli = 12345;   // 12.345 units

// Later
r.type = VAL_REAL;
r.value.real = 12.345f;
Tagged unions (enum + union in a struct) are a common pattern in C to safely handle data that can take multiple forms.

Example 2 – Breaking a 32-bit Value into Bytes

In embedded or communication code, you might want to send a 32-bit integer as 4 bytes over a serial line. A union can make this convenient:

#include <stdio.h>
#include <stdint.h>

union U32Bytes {
    uint32_t value;
    unsigned char bytes[4];
};

int main() {
    union U32Bytes u;
    u.value = 0x12345678;

    printf("Bytes: %02X %02X %02X %02X\\n",
           u.bytes[0], u.bytes[1], u.bytes[2], u.bytes[3]);

    return 0;
}

Here the same memory is accessed as a 32-bit integer and as a 4-byte array. Note that endianness (byte order) depends on the CPU, so this is platform-specific – but this pattern is widely used in low-level code.

Caution: Dangers & Limitations of Unions

  • Only one active member: You must know which member is currently valid. That’s why we often use an enum tag.
  • Strict aliasing & portability: Reinterpreting memory as a different type can be non-portable and must be done carefully, especially with floating-point types.
  • Alignment: The union must be aligned for all member types; compilers handle this, but the size may be bigger than the largest member due to padding.
If you write to u.f and then read from u.i (with different meanings), the result is often implementation-defined or undefined behaviour. Only use union type-punning when you understand the platform and compiler rules.

Where & Why to Use Enums vs Unions (Summary)

Use enums when…

  • You have a small set of states (modes, errors, menu options).
  • You currently use #define or magic numbers for codes.
  • You want clearer, self-documenting code.
  • You write switch-based state machines.

Use unions when…

  • You need one of several data formats, but only one at a time.
  • You work in memory-constrained systems (embedded, microcontroller).
  • You implement protocols or packets with variant payloads.
  • You need to reinterpret the same bytes as different views (with care).

Practice Tasks – Unions & Enums

  1. Enum-based Menu: Create an enum for menu choices and write a switch-based menu program that uses these enum constants instead of numbers.
  2. Error Handling: Define an enum ErrorCode and use it as the return type of functions. Print meaningful messages based on the enum values.
  3. Tagged Union for Measurements: Create a struct that contains an enum type and a union of int/float, then write a function to print the value based on the type.
  4. Packet Union: Design a union representing a packet payload that can either be: (a) three uint8_t bytes, or (b) one uint16_t + one uint8_t. Use the union to fill and print the bytes.
  5. Enum + Array Table: Define an enum for days of the week and an array that stores work hours for each day. Print a weekly report using the enum names.

Suggested Featured Image Prompt

Use this in your AI image generator for the Tamil Technicians blog thumbnail:

“Flat modern 16:9 illustration on a light background. On the left, a memory block is drawn as a single horizontal bar labeled ‘union memory’, with overlapping labels `int`, `float`, and `bytes[4]` showing that different views share the same space. Above it, small C code text shows `union Data { int i; float f; unsigned char bytes[4]; };`. On the right, a vertical list of colored tags represents an enum with items like `MODE_OFF`, `MODE_IDLE`, `MODE_RUN`, each with small icons (power button, pause, play). At the bottom, a South Indian / Tamil technician character is pointing to the union memory diagram with one hand and to the enum list with the other, with a small caption ‘Where & Why to Use Them?’. At the top, bold title text: ‘Unions & Enums in C’ and smaller subtitle: ‘Where and Why to Use Them? | Tamil Technicians’. Clean vector style, minimal colors, educational, high quality.”

FAQ: Unions & Enums in C

1. Are enums just integers in C?

Yes. In C, enums are essentially integers under the hood. Each enum constant has an integer value, usually starting from 0 and increasing by 1 unless you specify values. However, using enums improves readability and helps group related constants together.

2. Can I print an enum directly with printf?

You can print an enum using %d because it is stored as an integer: printf("%d", mode);. To print the name (like “MODE_RUN”), you must write a helper function that maps each enum value to a string.

3. Can a union store all members at the same time?

Physically, all members occupy the same memory region, but logically only one member should be considered valid at a time. If you write to one member and then read from another with a different type, the result is usually undefined, unless you are doing careful low-level type punning and know the rules.

4. Are unions necessary for normal application programming?

For many high-level applications, you can write perfectly fine C without unions. But in embedded systems, protocol parsing, and memory-critical code, unions are very helpful. Even if you don’t use them daily, understanding unions will make you a more complete C programmer.

Category: C Programming Course · Lesson 13 – Unions & Enums in C

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top