Dataslope logoDataslope

Structs

Bundling related values into a single named type

A program's data rarely comes in lone variables. A point has an x and a y. A student has a name, an id, and a GPA. A date has a year, month, and day. C's tool for bundling related values into a single named thing is the struct.

Without structs you'd be passing around parallel arrays or huge parameter lists and praying nothing fell out of sync. With structs, each "thing" becomes a single, named, typed value you can move around as a unit.

Declaring a struct

struct Point {
    int x;
    int y;
};

This declares a type called struct Point with two fields (also called members): x and y. It does not yet create any variables. To create one:

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

Field access uses a dot. You can also initialize at declaration:

struct Point p = {3, 4};                    // positional
struct Point q = { .x = 3, .y = 4 };        // designated (clearer)

Designated initializers are usually worth the extra characters — they make the code readable and survive field reordering.

typedef to drop the struct keyword

Writing struct Point over and over is noisy. The idiomatic remedy is a typedef:

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

Point p = { .x = 3, .y = 4 };

Now Point is the type name. You'll see both styles in real code; the typedef'd form is more common in beginner-friendly tutorials and in modern C code.

Code Block
C 17 (201710L)

Passing structs to functions

You can pass a struct by value (a copy is made):

int dist_sq(Point p) {
    return p.x * p.x + p.y * p.y;
}

Or, for bigger structs or when you want to modify the original, pass a pointer:

void shift(Point *p, int dx, int dy) {
    (*p).x += dx;
    (*p).y += dy;
}

That (*p).x is so common that C has a special operator for it: the arrow ->.

void shift(Point *p, int dx, int dy) {
    p->x += dx;
    p->y += dy;
}

p->x means "the field x of the struct that p points at". It's exactly equivalent to (*p).x but easier to read.

Through a valueThrough a pointer
p.xp->x or (*p).x
Code Block
C 17 (201710L)

A bigger example: a student record

Code Block
C 17 (201710L)

Things to notice:

  • An array of structs stores them back-to-back in memory, the same way an array of ints does.
  • The string fields are fixed-size character arrays — they live inside the struct.
  • const Student *s says "I want a pointer to a student, and I promise not to modify it". The const is documentation enforced by the compiler.

Memory layout of a struct

A struct's fields are stored in declaration order, with possible padding between them so each field is properly aligned for its type:

sizeof(Student) will be at least 4 + 32 + 4 = 40 bytes, and may be slightly larger because of alignment. Use sizeof rather than hardcoding numbers.

Nested structs

Structs can contain other structs as fields. This is how you build up rich data without leaving C's type system.

Code Block
C 17 (201710L)

Read box.top_left.x as "the x of the top_left of box". Dot chains work just like in everyday English.

Struct assignment copies

Assigning one struct to another copies every field:

Point a = {1, 2};
Point b = a;     // b is now its own (1, 2)
b.x = 99;        // a is unchanged

This is convenient but be aware: copying a struct that contains a pointer copies the pointer, not the thing it points to. Both copies would then "own" the same heap allocation, which is a recipe for double-frees. We'll meet this pattern again in the linked-list chapter.

Challenge: rectangle area

Challenge
C 17 (201710L)
Compute the area of a rectangle

Define a Rect struct with integer fields width and height. Write a function int area(const Rect *r) that returns r->width * r->height. The provided main builds a 4x5 rectangle and prints the area.

The program must print exactly 20.

QuestionSelect one

Given Point *p = &some_point;, which expression accesses the field x?

p.x

*p.x

p->x

&p.x

QuestionSelect one

What does this code print?

typedef struct { int a; int b; } S;
S x = {1, 2};
S y = x;
y.a = 99;
printf("%d %d\n", x.a, y.a);

Hint: the printf prints x.a first, then y.a. Decide whether the assignment to y changes x.

99 99

99 1

1 99

The code is invalid; you can't assign one struct to another.

On this page