5.1.22 · D4C Programming

Exercises — Structures — declaration, accessing members (. and - - )

2,136 words10 min readBack to topic

The picture below is the single mental model for the whole page — glance at it whenever you're unsure which operator to reach for.

Figure — Structures — declaration, accessing members (. and - - )

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 → arrow

Prints 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.x is now 10.
  • p->y reads a.y, still 4. Prints 10 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 = a makes an independent copy. Editing b.x never touches a.
  • p = &a makes an alias — same object. p->y = 200 edits a.y. So a.x stays 1, a.y becomes 200. Prints 1 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 stick

Trace: q points at a. q->x += 1a.x becomes 4. q->y += 1a.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.)

Figure — Structures — declaration, accessing members (. and - - )

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, then p = a.next = &b.
  • p = &b: sum = 10 + 20 = 30, then p = b.next = &c.
  • p = &c: sum = 30 + 30 = 60, then p = c.next = NULL.
  • p == NULL → loop ends. Prints 60.

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 &a above 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) but s.roll = 42 works.
  • typedef — lets you drop the repeated word struct.
  • Memory Alignment & Padding — why the byte offsets in the top figure may have gaps.