Step 1 of 5 · Reading · ~4 min
Learn
Memory and Structs
Structs
Arrays collect many of the same thing; structs collect the different parts of one thing. It's C's only tool for building compound types — no classes, no objects — and paired with functions and pointers it turns out to be enough: most of the abstractions you know from "bigger" languages are structs wearing conventions.
Defining and using
struct Point {
int x;
int y;
};
struct Point p = {3, 4}; /* positional init */
struct Point q = {.x = 1, .y = 2}; /* designated init — clearer, use it */
p.x = 10; /* dot: access a member */
int dx = p.x - q.x;
The type's full name is struct Point — the keyword travels with it everywhere the type is written. Members are laid out in order, adjacently in memory: a struct is a little contiguous record, the same honest bytes-in-a-row story as arrays.
typedef: the spelling most C code actually uses
Writing struct in front of the type forever gets old, so C code usually names the type once:
typedef struct {
int x, y;
} Point; /* an anonymous struct + a type name for it */
Point p = {3, 4}; /* no `struct` keyword */
Point q = {.x = 1, .y = 2};
Both styles are everywhere in real code, and this lesson's exercise starter uses the second one. Mixing them is the mistake that breaks more submissions on this exercise than anything else. After the typedef above there is no tag named Point — only a type alias — so asking for struct Point names a type the compiler has never heard of:
error: variable 'p' has initializer but incomplete type
error: storage size of 'p' isn't known
The rule is one line: if you wrote struct Point { … };, you say struct Point p; from then on; if you wrote typedef struct { … } Point;, you say Point p;.
Structs are values
Assignment copies the whole thing, member by member:
Point a = {1, 2};
Point b = a; /* full copy */
b.x = 99;
printf("%d\n", a.x); /* 1 — a untouched */
Same for function calls: pass a struct, the function gets a copy (all of chapter 4 applies). For a two-int Point, copying is cheap and good — no aliasing surprises. For big structs, or when the function must modify the original, pass a pointer — which brings us to C's best piece of syntactic sugar:
The arrow
void move_right(Point *p, int dist) {
p->x += dist; /* p->x ≡ (*p).x — follow pointer, take member */
}
move_right(&pt, 5);
-> exists because (*p).x is miserable to type and the pattern is everywhere: pointer-to-struct is the default calling convention for anything non-trivial. Reading rule: dot when you hold the struct, arrow when you hold its address. Miss it and the compiler's error (request for member in something not a structure) at least points at the right line.
This function-taking-struct-pointer shape — move_right(&pt, …), player_heal(&p, 20) — is, honestly, most of what "methods" are: the pointer parameter that OO languages spell this and pass invisibly, C spells out loud. Once you see it, object systems stop being magic.
Structs compose
typedef struct {
Point center; /* structs nest */
int radius;
} Circle;
Circle c = {{0, 0}, 5};
c.center.x /* chained dots */
Records of records — plus arrays of structs (Point path[100];) — cover an enormous amount of real modeling before anything fancier is needed.
Your exercise: Point Distance Squared
Read two points, print (x₂−x₁)² + (y₂−y₁)². No square root, which keeps the whole exercise in integers: the starter's Point has int members, the differences are int, and the answer prints with %d — no floating-point formatting to get wrong.
Structure it like you mean it: build two Point values from the four numbers main read (Point p = {x1, y1}; — the starter typedef'd it, so no struct keyword), subtract member by member, and keep the I/O in main. Scanning straight into members works too, exactly like variables: scanf("%d %d", &p.x, &p.y) — the & reaches inside a struct just fine.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…