C Cheatsheet

Structs and Unions

Use this C reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

struct Declaration and Definition

// Declare a struct type
struct Point {
    int x;
    int y;
};

// Declare a variable at the same time
struct Rectangle {
    int width;
    int height;
} rect1, rect2;

// Usage
struct Point p;
p.x = 3;
p.y = 4;

struct Point q = {10, 20};         // positional initializer
struct Point r = {.x = 10, .y = 20}; // designated initializer (C99+)

typedef with struct

// Separate typedef
struct Node {
    int data;
    struct Node *next;    // self-referential — must use struct tag here
};
typedef struct Node Node;

// Combined (common idiom)
typedef struct {
    float x;
    float y;
    float z;
} Vec3;

Vec3 v = {1.0f, 2.0f, 3.0f};
printf("%f\n", v.x);

Member Access

struct Point p = {3, 4};
struct Point *pp = &p;

p.x          // access member of struct object
pp->x        // access member via pointer (equivalent to (*pp).x)
(*pp).y      // same as pp->y

Struct Initialization

typedef struct {
    int id;
    char name[64];
    double score;
    int active;
} Student;

// Positional
Student s1 = {1, "Alice", 95.5, 1};

// Designated (C99+) — order-independent, unspecified fields = 0
Student s2 = {.name = "Bob", .score = 88.0, .id = 2};

// Zero-initialize all fields
Student s3 = {0};

// Compound literal (anonymous temporary)
Student *sp = &(Student){.id = 3, .name = "Carol", .score = 91.0};

Nested Structs

typedef struct {
    float x, y;
} Point2D;

typedef struct {
    Point2D origin;
    Point2D size;
} Rect;

Rect r = {{0, 0}, {100, 50}};
r.origin.x = 10;
printf("%f\n", r.size.y);

// Pointer to nested
Rect *rp = &r;
rp->origin.y = 20;

Arrays of Structs

Student students[3] = {
    {1, "Alice", 95.5, 1},
    {2, "Bob",   88.0, 1},
    {3, "Carol", 91.0, 0},
};

for (int i = 0; i < 3; i++) {
    printf("%s: %.1f\n", students[i].name, students[i].score);
}

// Dynamic array
Student *roster = malloc(n * sizeof(Student));
roster[0].id = 1;
free(roster);

Passing Structs to Functions

// By value — function gets a copy
void print_point(struct Point p) {
    printf("(%d, %d)\n", p.x, p.y);
}

// By pointer — function can modify, and no copy overhead
void translate(struct Point *p, int dx, int dy) {
    p->x += dx;
    p->y += dy;
}

// By const pointer — read-only, no copy
double distance(const struct Point *a, const struct Point *b) {
    int dx = a->x - b->x, dy = a->y - b->y;
    return sqrt(dx*dx + dy*dy);
}

// Return struct by value (small structs — fine; compilers optimize)
struct Point midpoint(struct Point a, struct Point b) {
    return (struct Point){(a.x+b.x)/2, (a.y+b.y)/2};
}

struct Padding and Size

// Compiler inserts padding for alignment
struct Padded {
    char  c;    // 1 byte
                // 3 bytes padding
    int   n;    // 4 bytes
    char  d;    // 1 byte
                // 7 bytes padding
    double f;   // 8 bytes
};
// sizeof(struct Padded) is likely 24, not 14

// Pack to eliminate padding (GCC/Clang extension)
struct __attribute__((packed)) Packed {
    char c;   // 1 byte
    int  n;   // 4 bytes — unaligned, may be slow
};
// sizeof == 5

// Reorder to reduce padding
struct Efficient {
    double f;  // 8
    int    n;  // 4
    char   c;  // 1
    char   d;  // 1
               // 2 bytes padding to align to 8
};

// Check sizes at compile time
_Static_assert(sizeof(struct Packed) == 5, "");

Flexible Array Members (C99+)

struct Header {
    int length;
    char data[];    // flexible array member — must be last
};

struct Header *h = malloc(sizeof(struct Header) + 20);
h->length = 20;
h->data[0] = 'A';
free(h);

union

All members share the same memory. The size equals the largest member.

union Data {
    int   i;
    float f;
    char  bytes[4];
};

union Data d;
d.i = 0x41424344;
printf("%c\n", d.bytes[0]);  // prints 'D' on little-endian ('A' on big-endian)

// Only the last-written member should be read (others are UB in C++ but OK in C)
d.f = 3.14f;
printf("%f\n", d.f);   // ok
printf("%d\n", d.i);   // type-punning: valid in C, reads float bits as int

Tagged Union (Discriminated Union)

typedef enum { TYPE_INT, TYPE_FLOAT, TYPE_STRING } Tag;

typedef struct {
    Tag tag;
    union {
        int    i;
        float  f;
        char  *s;
    } value;
} Variant;

void print_variant(const Variant *v) {
    switch (v->tag) {
        case TYPE_INT:    printf("%d\n",  v->value.i); break;
        case TYPE_FLOAT:  printf("%f\n",  v->value.f); break;
        case TYPE_STRING: printf("%s\n",  v->value.s); break;
    }
}

Anonymous Structs and Unions (C11+)

// Anonymous union inside struct — members accessed directly
typedef struct {
    int type;
    union {
        int   ival;
        float fval;
        char *sval;
    };   // no name — ival/fval/sval accessed on the outer struct
} Value;

Value v;
v.type = 0;
v.ival = 42;   // no "v.u.ival" needed

Bit Fields

struct Flags {
    unsigned int read    : 1;   // 1 bit
    unsigned int write   : 1;   // 1 bit
    unsigned int execute : 1;   // 1 bit
    unsigned int level   : 4;   // 4 bits
    unsigned int         : 0;   // force new storage unit
    unsigned int extra   : 8;
};

struct Flags f = {0};
f.read = 1;
f.level = 7;   // max value for 4 bits

// Bit fields cannot be addressed with &
// Layout is implementation-defined — avoid for portable wire formats

struct vs union Memory

struct S { int a; int b; };     // sizeof = 8 (both stored)
union U  { int a; int b; };     // sizeof = 4 (share same 4 bytes)

struct S s; s.a = 1; s.b = 2;  // both accessible simultaneously
union U  u; u.a = 1; u.b = 2;  // u.b overwrites u.a (same memory)

Forward Declaration

// Forward declare to use in pointers before full definition
struct Node;                        // incomplete type
struct Node *create_node(void);     // pointer is fine

// Full definition later
struct Node {
    int data;
    struct Node *next;
};