Industrial manufacturing
Industrial Internet of Things | Industrial materials | Equipment Maintenance and Repair | Industrial programming |
home  MfgRobots >> Industrial manufacturing >  >> Industrial Internet of Things >> Embedded

Embedded C Unions Explained: Efficient Memory Sharing & Messaging

Understanding Unions in Embedded C

In embedded systems, a union is a powerful data structure that lets you store different data types in the same memory location. This article walks through the fundamentals of unions, shows practical examples, and explains why they are a go-to tool for memory‑constrained and bandwidth‑sensitive applications.

The Difference Between Structure and Union in Embedded C

Earlier, we covered structures: a way to group heterogeneous variables into a single entity. In C, unions are a complementary construct that also groups different types, but with a key distinction—each member shares the same memory address.

Introductory Example

Declaring a union is almost identical to declaring a structure; you simply replace struct with union:

union test {
    uint8_t  c;
    uint32_t i;
};

The template defines two members: c occupies one byte, and i occupies four bytes. To create a variable of this type:

union test u1;

Accessing members uses the dot operator:

u1.i = 10;
m = u1.c;   // m must be uint8_t

Because a union’s storage size equals the size of its largest member, u1 occupies four bytes. Writing to i overwrites the same memory that c would use, so the two members are mutually exclusive.

Do We Need Shared Memory Space?

Imagine two embedded devices, Device A and Device B, that exchange status, velocity, and position data (see Figure 1). The data items and their sizes are:

Embedded C Unions Explained: Efficient Memory Sharing & Messaging

Figure 1
Variable Size (bytes) Meaning
power 1 Battery charge
op_mode 1 Operating mode
temp 1 Ambient temperature
x_pos 2 X‑coordinate
y_pos 2 Y‑coordinate
vel 2 Velocity

If Device B requires every piece of information, a simple structure would need nine bytes, and each communication round would transfer a 9‑byte frame (see Figure 2). However, in many real‑world scenarios, the three message types are mutually exclusive: sometimes only status, sometimes only position, sometimes only velocity. In that case, the largest payload is four bytes (the position message). A union that shares a four‑byte buffer can hold any of the three messages, reducing the frame size from nine to four bytes (see Figure 3).

Embedded C Unions Explained: Efficient Memory Sharing & Messaging

Figure 2

Embedded C Unions Explained: Efficient Memory Sharing & Messaging

Figure 3

Using Unions for Message Packets

Below we create two structures—one for status and one for position—and embed them in a union alongside the velocity field:

struct status_s {
    uint8_t power;
    uint8_t op_mode;
    uint8_t temp;
};

struct position_s {
    uint16_t x_pos;
    uint16_t y_pos;
};

union msg_union {
    struct status_s status;
    struct position_s position;
    uint16_t vel;
};

The union’s size is four bytes (the size of position_s), which is shared among all three message types.

How to Keep Track of the Union Active Member

When a receiver reads a union, it must know which member is valid. The common pattern is a tagged union: a separate field that records the active member. We can add a one‑byte msg_type before the union:

struct message_s {
    uint8_t msg_type;   // 's' = status, 'p' = position, 'v' = velocity
    union {
        struct status_s status;
        struct position_s position;
        uint16_t vel;
    } msg;
};

By transmitting the msg_type byte alongside the union, the recipient can correctly interpret the payload without prior knowledge of its shape.

The Alternative Solution: Dynamic Memory Allocation

Another approach is to allocate memory on the fly for each message type. The transmitter would set msg_type and allocate just enough bytes for that payload. The receiver would then interpret the incoming data based on msg_type and free the memory when finished. While this can be slightly more memory‑efficient, dynamic allocation introduces runtime overhead and the need for explicit deallocation, which is why many embedded developers prefer the static union strategy.

Next Up: Applications of Unions

Unions are also frequently used to extract smaller pieces of data from a larger word—a technique especially valuable when parsing communication protocols or hardware registers. Stay tuned for the next article in this series, where we dive into those use cases.

To explore more articles on embedded C, visit the article index.

Embedded

  1. Safeguard Off‑Site Firmware Programming for Your Embedded Devices
  2. Mastering UART: The Universal Asynchronous Receiver/Transmitter Explained
  3. Mastering Digital Filtering on Embedded Microcontrollers: PowerQuad’s Dual Biquad IIR Engine in the LPC55S69
  4. Using Unions in C for Efficient Data Packing and Unpacking
  5. Python for Electrical Engineers: Practical Applications Across Design, Testing, and Production
  6. Embedded C Structures: A Practical Guide to Definition, Usage, and Memory Optimization
  7. Discover the 9 Must‑Learn Programming Languages of 2021
  8. Mastering G and M Codes: A Comprehensive Guide to CNC Programming
  9. Understanding Robot Programming: Methods Explained
  10. 74LS47 IC Explained: BCD to 7-Segment Decoder Details