Difference between structure and Union
Difference between structure and Union

Structure and Union both are used to create a custom data type. As C language is a general-purpose, procedural computer programming language we often need functionality similar to OO language like C++. This is obtained through structure and interfaces.

Structure in C

Structure is used to create a custom data type that groups a number of primitive ones. It is more like several data type elements are packed and created into one data type.

Address of all members is different in memory.

Declaration

struct address
{
   char name;
   char street;
   int city_id;
   int state_id;
   long description;
};
Structure in C memory layout

Size of structure

The size of the structure element is the combined size of all elements inside it. That will come to 18 bytes. You can test with the compiler.

#include<stdio.h>

struct address
{
   char name;
   char street;
   int city_id;
   int state_id;
   long description;
};
int main(void) {
    struct address addr;
    printf("%d\n",sizeof(address));
    return 0;
}

Success — 24 bytes output
18 byte + 6 unused bytes added by compiler for alignment in memory.

Structure size in memory

Union in C

Union is used to create a custom data type that is a union of all members declared into it. Basically a data type of minimum size that can hold a value of any kind of its member.

Address of all members is always the same in memory.

Declaration

union address
{
   char name;
   char street;
   int city_id;
   int state_id;
   long description;
};

Size of Union

Size of union is the size of the largest member.

Union in C memory layout
#include<stdio.h>

union address
{
   char name;
   char street;
   int city_id;
   int state_id;
   long description;
};
int main(void) {
    union address addr;
    printf("%d\n",sizeof(addr));
    return 0;
}

Success — 8 bytes output
8 byte = long size (largest member)

Key Differences — struct vs union

Property struct union
Memory allocation Separate block for each member Single shared block (size of largest member)
sizeof Sum of all members (+ padding) Size of largest member (+ padding)
Member addresses All different All the same
Active members at once All members valid simultaneously Only one member valid at a time
Default initialisation = {0} zeroes all members = {0} zeroes only the first member
Typical use Records with multiple independent fields Type-punning, variant data, memory-mapped registers
C++ inheritance Allowed (can have base class) Not allowed in standard C++

Memory Layout Diagram

Consider both types defined with the same three members:

#include <stdio.h>

struct Demo { char a; int b; double c; };
union  Demo { char a; int b; double c; };

/*  struct Demo layout (typical 64-bit, with padding):
    ┌────┬───┬───┬───┬────────────────────┐
    │ a  │pad│pad│pad│    b (4 bytes)      │  ← a=1, pad=3, b=4
    ├────────────────────────────────────────┤
    │              c (8 bytes)               │  ← c=8
    └────────────────────────────────────────┘
    sizeof(struct Demo) = 16 bytes

    union Demo layout:
    ┌────────────────────────────────────────┐
    │           shared 8 bytes               │
    │  a (1)                                 │
    │  b (4)                                 │
    │  c (8) ← largest, sets union size      │
    └────────────────────────────────────────┘
    sizeof(union Demo) = 8 bytes
*/

Writing to one union member and then reading another is type-punning — defined behaviour in C (C99 §6.5.2.3) but undefined behaviour in C++ unless you use memcpy or std::bit_cast (C++20).

Accessing Members

Dot notation (.) for stack variables, arrow notation (->) for pointers — identical syntax for both struct and union.

#include <stdio.h>

struct Point { int x; int y; };
union  Data  { int i; float f; char c; };

int main(void) {
    /* struct — all members valid simultaneously */
    struct Point p = { .x = 10, .y = 20 };
    printf("x=%d, y=%d\n", p.x, p.y);   /* x=10, y=20 */

    /* union — only last-written member is valid */
    union Data d;
    d.i = 65;
    printf("i=%d, c=%c\n", d.i, d.c);   /* i=65, c=A (same bytes!) */
    d.f = 3.14f;
    printf("f=%.2f\n", d.f);            /* f=3.14 */
    /* d.i is now garbage — f overwrote those bytes */

    /* pointer syntax */
    struct Point *pp = &p;
    printf("x=%d\n", pp->x);            /* arrow for pointers */

    return 0;
}

Embedded Systems Use Cases

Unions are especially common in embedded C for two patterns:

1. Memory-mapped register overlay

Access a hardware register both as a raw 32-bit word and as individual bitfields — without any casting:

/* UART status register — 32-bit peripheral register */
typedef union {
    uint32_t word;          /* full register access */
    struct {
        uint32_t rx_ready : 1;
        uint32_t tx_empty : 1;
        uint32_t overrun  : 1;
        uint32_t reserved : 29;
    } bits;
} UartStatus;

volatile UartStatus *uart = (UartStatus *)0x40011000;

if (uart->bits.rx_ready) {
    char ch = read_fifo();
}
uart->word = 0;   /* clear entire register in one write */

2. Protocol message variant

A single buffer that can hold different message types — common in CAN bus and serial protocol parsers:

typedef enum { MSG_SENSOR, MSG_CMD, MSG_ACK } MsgType;

typedef struct {
    MsgType type;
    union {
        struct { uint16_t sensor_id; int32_t value; } sensor;
        struct { uint8_t  cmd;       uint8_t  param; } cmd;
        struct { uint8_t  status;                    } ack;
    } payload;
} Message;

void handle(Message *m) {
    switch (m->type) {
        case MSG_SENSOR: printf("sensor %d = %d\n",
                                m->payload.sensor.sensor_id,
                                m->payload.sensor.value); break;
        case MSG_CMD:    execute(m->payload.cmd.cmd,
                                 m->payload.cmd.param);   break;
        case MSG_ACK:    check_status(m->payload.ack.status); break;
    }
}

Quick Reference

Taskstructunion
Declare variablestruct Point p;union Data d;
Initialise all= {.x=1, .y=2}= {.i = 42} (first member)
Member accessp.xd.i
Pointer accesspp->xdp->i
sizeofSum + paddingLargest + padding
typedef shorthandtypedef struct { … } Point;typedef union { … } Data;
Nested usagestruct inside unionunion inside struct

📬 Get new articles in your inbox

Deep dives on SystemC, C++, and embedded systems — no spam, unsubscribe any time.

No spam, unsubscribe any time. Privacy Policy

Aditya Gaurav

Aditya Gaurav

Embedded systems engineer specializing in SystemC, ARM architecture, and C/C++ internals. Writing deep technical dives for VLSI and embedded engineers.