Union declaration

From cppreference.com
< c‎ | language

A union is a type consisting of a sequence of members whose storage overlaps (as opposed to struct, which is a type consisting of a sequence of members whose storage is allocated in an ordered sequence). The value of at most one of the members can be stored in a union at any one time.

The type specifier for a union is identical to the struct type specifier except for the keyword used:

[edit] Syntax

union name(optional) { struct-declaration-list } (1)
union name (2)
name - the name of the union that's being defined
struct-declaration-list - any number of variable declarations, bit field declarations, and static assert declarations. Members of incomplete type and members of function type are not allowed.

[edit] Explanation

The union is only as big as necessary to hold its largest member (additional unnamed trailing padding may also be added). The other members are allocated in the same bytes as part of that largest member.

A pointer to a union can be cast to a pointer to each of its members (if a union has bit field members, the pointer to a union can be cast to the pointer to the bit field's underlying type). Likewise, a pointer to any member of a union can be cast to a pointer to the enclosing union.

The value of the bytes that correspond to union members other than the one last stored into is unspecified.

Similar to struct, an unnamed member of a union whose type is a union without name is known as anonymous union. Every member of an anonymous union is considered to be a member of the enclosing struct or union. This applies recursively if the enclosing struct or union is also anonymous.

struct v {
   union { // anonymous union
      struct { int i, j; }; // anonymous structure
      struct { long k, l; } w;
   };
   int m;
} v1;
 
v1.i = 2;   // valid
v1.k = 3;   // invalid: inner structure is not anonymous
v1.w.k = 5; // valid

Similar to struct, the behavior of the program is undefined if union is defined without any named members (including those obtained via anonymous nested structs or unions).

(since C11)

[edit] Example

This example exhibits the active member of a union, casting between union and member pointers, and unnamed trailing padding.

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
 
union S {
    int32_t n;       /* occupies 4 bytes */
    uint16_t s[2];   /* occupies 4 bytes */
    uint8_t c;       /* occupies 1 byte  */
}; /* the whole union occupies 4 bytes */
 
/* padded union */
union pad {          /* occupies 8 bytes */
       char  c[5];   /* occupies 5 bytes */
       float f;      /* occupies 4 bytes */
} p;
 
int main(void)
{
    /* Initialize the first member of s; s.n is now the active member. */
    union S s = {0x12345678};
    printf("sizeof(union S) = %zu\n", sizeof(union S));
 
    /* At this point, reading from s.s or s.c is UB. */
    printf("s.n = %x\n", s.n);
 
    /* Assign a value to s.s[0]. */
    s.s[0] = 0x0011;   // s.s is now the active member
 
    /* At this point, reading from n or c is UB, but most compilers define this. */
    printf("s.c is now %x\n", s.c);  // 11 or 00, depending on platform
    printf("s.n is now %x\n", s.n);  // 12340011 or 00115678
 
    /* Define a pointer to a union.              */
    /* Initialize the uint8_t member.            */
    /* Print the uint8_t member via the pointer. */
    union S *ps=&s;
    s.c = 0;
    printf("%x  %x\n",ps->c,(*ps).c);
 
    /* A pointer to a union can be cast to a pointer to each of its members. */
    uint8_t *p8 = &s.c;   /* pointer to the uint8_t member */
    p8 = (uint8_t*)ps;    /* cast union pointer to member pointer */
 
    /* A pointer to any member of a union can be cast to a pointer to the enclosing */
    /* union.                                                                       */
    ps = (union S*)p8;    /* cast member pointer to union pointer */
 
    /* Union members are allocated memory at the same starting address. */
    printf("offset of char c[] = %zu\noffset of float f  = %zu\n",
           offsetof(union pad, c), offsetof(union pad, f));
 
    /* Union pad has 3 bytes of unnamed trailing padding. */
    printf("sizeof(union pad) = %lu\n", sizeof(union pad));   /* 8 bytes */
    printf("sizeof(p.c)       = %lu\n", sizeof(p.c));         /* 5 bytes */
    printf("sizeof(p.f)       = %lu\n", sizeof(p.f));         /* 4 bytes */
 
    return 0;
}

Possible output:

sizeof(union S) = 4
s.n = 12345678
s.c is now 11
s.n is now 12340011
0  0
offset of char c[] = 0
offset of float f  = 0
sizeof(union pad) = 8
sizeof(p.c)       = 5
sizeof(p.f)       = 4
Each member is allocated as if it were the only member of the union, which is why s.c in the example above aliases the first byte of s.s[0].