C Structs & Unions Cheat Sheet
Explains defining and using C structs and unions, including member access, typedef, padding, bit-fields, and memory layout differences.
Struct Basics
Declaring, initializing, and accessing struct members.
struct Point { int x; int y;};struct Point p1 = {10, 20}; // Aggregate initializationp1.x = 30; // Access member with the dot operatorstruct Point *ptr = &p1;ptr->y = 40; // Access member through a pointer with ->
typedef and Designated Initializers
Common patterns for naming and initializing structs.
typedef struct { char name[32]; int age;} Person;Person alice = {.name = "Alice", .age = 30}; // Designated initializers (C99)Person people[3]; // Array of structspeople[0] = alice;
Unions
Multiple members sharing the same storage.
union Value { int i; float f; char bytes[4];};union Value v;v.i = 65; // Only one member is valid at a timeprintf("%d\n", v.i);v.f = 3.14f; // Overwrites v.i; the same memory is reusedprintf("%f\n", v.f);
Struct vs Union
Key differences in layout and sizing.
- struct- Each member has its own storage; total size >= sum of member sizes plus padding
- union- All members share the same storage; size equals the size of the largest member
- Padding- The compiler may insert padding bytes between members for alignment
- sizeof- Use sizeof(struct T) to get the actual allocated size, including padding
- Self-referential structs- A struct can contain a pointer to its own type, enabling linked lists and trees
- Bit-fields- struct Flags { unsigned int a: 1; unsigned int b: 3; }; packs fields into fewer bits
- Anonymous unions- C11 allows a union to be embedded in a struct without a member name
Alignment and Padding in Detail
How the compiler inserts padding bytes to satisfy each member's alignment requirement.
#include <stdio.h>#include <stddef.h>struct Bad { char a; // offset 0 int b; // offset 4 (3 bytes padding after a) char c; // offset 8}; // sizeof == 12 (3 trailing padding bytes)struct Good { int b; // offset 0 char a; // offset 4 char c; // offset 5}; // sizeof == 8 (reordering large-to-small shrinks padding)printf("Bad: %zu, Good: %zu\n", sizeof(struct Bad), sizeof(struct Good));printf("offset of b in Bad: %zu\n", offsetof(struct Bad, b));printf("align of int: %zu\n", _Alignof(int));
Bit-Fields for Packed Flags
Declaring, sizing, and reading individual bit-fields inside a struct.
struct StatusReg { unsigned int ready : 1; unsigned int error : 1; unsigned int mode : 3; // values 0-7 unsigned int : 2; // unnamed padding bits unsigned int priority : 4;}; // packs into a single unsigned int (implementation-defined layout)struct StatusReg reg = {0};reg.ready = 1;reg.mode = 5;if (reg.ready && !reg.error) { // Bit-field order and byte layout across members is // implementation-defined, so never memcpy/serialize a bit-field // struct across machines or compilers. printf("mode=%u\n", reg.mode);}
Flexible Array Members
A C99 idiom for variable-length trailing data allocated in one block.
struct Buffer { size_t length; char data[]; // flexible array member: must be the last field};struct Buffer *make_buffer(size_t n) { // Allocate the header plus n bytes for data in a single malloc struct Buffer *b = malloc(sizeof(struct Buffer) + n); if (b) { b->length = n; } return b; // one malloc/free pair instead of two separate allocations}// sizeof(struct Buffer) does NOT include the flexible array's storage
Anonymous Nesting and Tagged Unions
C11 anonymous unions/structs and the discriminated-union pattern for safe runtime dispatch.
enum ShapeKind { CIRCLE, RECTANGLE };struct Shape { enum ShapeKind kind; // discriminant / tag union { // C11 anonymous union: members promoted struct { double radius; } circle; struct { double w, h; } rectangle; }; // no member name needed to reach circle/rectangle};double area(const struct Shape *s) { switch (s->kind) { case CIRCLE: return 3.14159 * s->circle.radius * s->circle.radius; case RECTANGLE: return s->rectangle.w * s->rectangle.h; } return 0.0; // always switch on the tag before reading a union member}
Struct Memory & Layout Tools
Standard-library facilities for inspecting and comparing struct memory.
- offsetof(T, member)- <stddef.h> macro giving the byte offset of a member without creating an instance
- _Alignof / alignof- (C11, <stdalign.h>) reports the required alignment of a type in bytes
- _Alignas / alignas- Forces a stricter alignment on a struct or member than the default
- #pragma pack / __attribute__((packed))- Compiler-specific ways to remove padding; breaks natural alignment and portability
- memcmp on structs- Unsafe for equality checks: uninitialized padding bytes make two 'equal' structs compare unequal
- Compound literals- (struct Point){.x = 1, .y = 2} creates an unnamed struct value inline, useful in function calls
- Struct assignment- a = b performs a shallow memberwise copy; pointer/array members still alias the same memory
When passing a struct to a function, the entire struct is copied by value — pass a pointer (const struct Point *) for large structs to avoid the copy overhead.