Exercises — Structures — declaration, accessing members (. and - - )
The picture below is the single mental model for the whole page — glance at it whenever you're unsure which operator to reach for.

Level 1 — Recognition
Goal: given a name, decide instantly whether it's an object or an address, and pick the right operator.
L1.1 You have struct Point p;. Which is correct to read member x: p.x or p->x?
Recall Solution L1.1
p is a plain variable — you hold the object. Object → dot.
Answer: p.x. Writing p->x would give "invalid type argument of '->' (have 'struct Point')" because -> demands a pointer.
L1.2 You have struct Point *q; (a pointer). Which reads member y: q.y or q->y?
Recall Solution L1.2
q holds an address, not the object. Address → arrow.
Answer: q->y. q.y would give "request for member 'y' in something not a structure".
L1.3 Rewrite (*q).y using the arrow operator.
Recall Solution L1.3
By definition .
Answer: q->y. Same meaning, no parentheses needed.
Level 2 — Application
Goal: write correct member accesses in short code, including through a pointer.
L2.1 Declare struct Point { int x, y; };, make a variable a, and set a.x = 5, a.y = 9. Then make a pointer p to a and print x through the pointer. What line prints x?
Recall Solution L2.1
struct Point { int x, y; };
struct Point a;
a.x = 5; // a is object → dot
a.y = 9;
struct Point *p = &a; // p holds the ADDRESS of a
printf("%d", p->x); // p is pointer → arrowPrints 5. p->x walks to a then grabs x.
L2.2 Given the code below, what does it print?
struct Point { int x, y; };
struct Point a = {3, 4};
struct Point *p = &a;
p->x = 10;
printf("%d %d", a.x, p->y);Recall Solution L2.2
p points at the same object a (because of &a). So p->x = 10 edits a.x itself.
a.xis now10.p->yreadsa.y, still4. Prints10 4.
L2.3 s.name is a char[20]. Why is s.name = "Asha"; wrong, and what should you write?
Recall Solution L2.3
An array name is not assignable — you cannot overwrite the whole array with =. You must copy the characters in:
strcpy(s.name, "Asha");Answer: use strcpy(s.name, "Asha"); (needs #include <string.h>).
Level 3 — Analysis
Goal: trace pointers, nested structs, and copy-vs-alias behaviour.
L3.1 Nested access. Given
struct Date { int d, m, y; };
struct Event { char title[10]; struct Date when; };
struct Event e;
struct Event *pe = &e;Write the expression for year y: (a) using e, (b) using pe.
Recall Solution L3.1
(a) e is an object → dot into when (also an object) → dot into y:
e.when.y
(b) pe is a pointer → arrow to reach .when, which is a value, then dot:
pe->when.y
Rule: the first hop depends on e vs pe; once you land on the inner value when, every further hop is a dot.
L3.2 Copy vs alias. What does this print?
struct Point { int x, y; };
struct Point a = {1, 1};
struct Point b = a; // copy
struct Point *p = &a; // alias
b.x = 100;
p->y = 200;
printf("%d %d", a.x, a.y);Recall Solution L3.2
b = amakes an independent copy. Editingb.xnever touchesa.p = &amakes an alias — same object.p->y = 200editsa.y. Soa.xstays1,a.ybecomes200. Prints1 200.
L3.3 Precedence puzzle. p is struct Point *. Does *p.x compile, and if so what does it mean?
Recall Solution L3.3
. binds tighter than *, so the compiler reads it as *(p.x). But p is a pointer, so p.x is already illegal (member access on a non-struct). It does not compile.
To dereference-then-access you must write (*p).x, or simply p->x. This precedence rule is exactly why the arrow operator exists.
Level 4 — Synthesis
Goal: build small functions that must choose the operator correctly to work at all.
L4.1 Write void grow(struct Point *q) that adds 1 to both fields of the caller's point. Then trace grow(&a) for a = {3,4}.
Recall Solution L4.1
void grow(struct Point *q) {
q->x += 1; // q is a pointer → arrow
q->y += 1;
}
struct Point a = {3, 4};
grow(&a); // pass the ADDRESS so changes stickTrace: q points at a. q->x += 1 → a.x becomes 4. q->y += 1 → a.y becomes 5.
After the call, a is (4, 5).
Had we written void grow(struct Point q) (by value), grow would edit a copy and a would stay (3,4).
L4.2 Write a function int distSq(struct Point p1, struct Point p2) returning the squared distance . Evaluate for (0,0) and (3,4).
Recall Solution L4.2
Here we take points by value — we only read them, so no pointers needed.
int distSq(struct Point p1, struct Point p2) {
int dx = p2.x - p1.x; // dot: p1, p2 are values
int dy = p2.y - p1.y;
return dx*dx + dy*dy;
}For (0,0) and (3,4): , so .
Returns 25. (We compute squared distance to stay in integers and avoid sqrt.)

Level 5 — Mastery
Goal: combine structs, pointers, nesting, and copy semantics in one reasoning chain — the linked-list flavour.
L5.1 Given
struct Node { int val; struct Node *next; };
struct Node a = {10, NULL};
struct Node b = {20, NULL};
a.next = &b;Write the expression that reads b's value starting from a. What is its value?
Recall Solution L5.1
a is an object → dot to next. a.next is a pointer (to b) → arrow to reach val:
a.next->val, which is 20.
This is exactly how you walk a linked list: each ->next hops to the next node.
L5.2 Full trace. What does this print?
struct Node { int val; struct Node *next; };
struct Node a = {10, NULL};
struct Node b = {20, NULL};
struct Node c = {30, NULL};
a.next = &b;
b.next = &c;
struct Node *p = &a;
int sum = 0;
while (p != NULL) {
sum += p->val;
p = p->next;
}
printf("%d", sum);Recall Solution L5.2
Walk the chain a → b → c → NULL:
p = &a:sum = 0 + 10 = 10, thenp = a.next = &b.p = &b:sum = 10 + 20 = 30, thenp = b.next = &c.p = &c:sum = 30 + 30 = 60, thenp = c.next = NULL.p == NULL→ loop ends. Prints60.
L5.3 Design + reason. You have a function void increment(struct Node *n) meant to do n->val += 5. A student calls it as increment(a.next). Given the setup of L5.1 (a.next = &b, b.val = 20), what is b.val after the call, and why does this work with just a.next (no &)?
Recall Solution L5.3
void increment(struct Node *n) { n->val += 5; }
increment(a.next); // a.next is ALREADY a pointer (= &b)a.next is itself an address (it holds &b), so it already matches the struct Node * parameter — no extra & needed. Inside, n aliases b, so n->val += 5 edits b.
After the call, b.val = 25.
Contrast: to pass the object a you'd write increment(&a); to pass an existing pointer field you pass it as-is.
Recall One-line self-test before you leave
Given X, which operator? X is a pointer ::: use ->
Given X, which operator? X is a plain variable/value ::: use .
a.next->val — why dot then arrow? ::: a is a value (dot), a.next is a pointer (arrow)
Modify caller's struct from a function — pass what? ::: the address &a, parameter is struct Node *
b = a between two structs does what? ::: an independent value copy, not a link
Connections
- Pointers in C — every
->and every&aabove rests on pointer basics. - Linked Lists — L5 is the core node-walking pattern.
- Passing Structures to Functions — the by-value vs by-pointer choice behind L4 and L5.
- Arrays vs Structures — why
s.name = "..."fails (array member) buts.roll = 42works. - typedef — lets you drop the repeated word
struct. - Memory Alignment & Padding — why the byte offsets in the top figure may have gaps.