Structs

A struct groups several related values, possibly of different types, under one name. Where an array holds many values of the same type reached by index, a struct holds a fixed set of named fields — the natural way to represent one real-world thing made of several parts.

Defining and using a struct

C main.c
#include <stdio.h>

struct Point {
    int x;
    int y;
};

int main(void) {
    struct Point origin = {0, 0};
    struct Point p1 = {3, 4};

    printf("origin = (%d, %d)\n", origin.x, origin.y);
    printf("p1 = (%d, %d)\n", p1.x, p1.y);

    return 0;
}
Output
origin = (0, 0)
p1 = (3, 4)

struct Point { ... } defines a new type — it doesn't create any variables by itself, just describes the shape. struct Point origin = {0, 0} then creates an actual variable of that type, and the dot (.) reaches into it to read or write an individual field.

typedef for a shorter name

Writing struct Point everywhere gets tedious. typedef lets you give the type a shorter alias:

C main.c
#include <stdio.h>

typedef struct {
    char name[32];
    int age;
} Person;

int main(void) {
    Person alice = {"Alice", 29};

    printf("%s is %d\n", alice.name, alice.age);

    return 0;
}
Output
Alice is 29

After this typedef, Person can be used anywhere a type name is expected, exactly like int or char — no more struct keyword required.

Pointers to structs and the arrow operator

Passing a large struct by value copies the whole thing. Passing a pointer to it instead is cheap, and -> combines the dereference and the field access into one step:

C main.c
#include <stdio.h>

typedef struct {
    int x;
    int y;
} Point;

void moveRight(Point *p, int amount) {
    p->x += amount;
}

int main(void) {
    Point p1 = {3, 4};

    moveRight(&p1, 10);
    printf("p1 = (%d, %d)\n", p1.x, p1.y);

    return 0;
}
Output
p1 = (13, 4)

p->x is shorthand for (*p).x — follow the pointer, then read the field. Because moveRight received the address of p1 rather than a copy, the change to x is visible back in main.

Note: the compiler is free to insert padding bytes between a struct's fields so each one lands at an address its type is happy with (an int field, for instance, is usually aligned to a 4-byte boundary). That means sizeof(struct Point) isn't always just the sum of its fields' sizes — reordering fields can sometimes shrink or grow a struct even though nothing about its data changed.