Visual walkthrough — Structures — declaration, accessing members (. and - - )
We will follow ONE tiny struct the whole way:
struct Point { int x, y; };Two members, x and y, each an int. Keep this in your head — every figure below draws exactly this.
Step 1 — Memory is just numbered boxes
WHAT: we draw memory as a ruler of boxes, addresses increasing left to right. WHY: every later idea (offset, pointer, dereference) is a way of talking about which box. If you can see the boxes, nothing else is mysterious. PICTURE: below, boxes 1000, 1001, 1002 … Each address is just a label on a box.

Step 2 — A struct variable is a block of boxes, in order
WHAT: we place a into memory starting at address 1000. a.x takes boxes 1000–1003, a.y takes 1004–1007.
WHY: "in order, next to each other" is what lets the compiler compute where a member is using only the start address and a fixed distance. No searching.
PICTURE: one blue block = a. Inside it, a yellow half = x, a green half = y.

- — the address where
abegins; call it the struct's base. - — first member, so it sits at the base (offset ).
- — second member, sitting 4 bytes past the base.
Step 3 — The dot . is really "base + offset"
WHAT: we show what a.y compiles down to: take the address of a, add the offset of y, read from there.
WHY: this is the single mechanism behind the dot. Once you see . = "walk offset bytes from the base and grab", the arrow will be the same walk with one extra step.
PICTURE: an arrow from the base box hopping bytes to land on y.

- — human-friendly name.
- — the base address, box (the
&means "address of"). - — offset of member
y, fixed forever at compile time. - "read at (...)" — go to that box and pull out the
int.
Step 4 — A pointer is a box that stores an address
WHAT: we add a new box p somewhere else in memory (say address 2000), and inside it we store the number .
WHY: in real programs (function arguments, linked lists) you often receive only this note, not the object. You must learn to reach the object through the note.
PICTURE: box p at 2000 holding "1000", with a long arrow flying over to the start of block a.

- — its own box (here at 2000); do not confuse it with
a. - — the value inside
p: a pointer's value is an address. - — that address happens to be where
abegins.
Step 5 — Dereferencing *p follows the note to the object
WHAT: we apply * to p. It reads out of p, travels to box 1000, and lands us on the struct itself.
WHY: we cannot grab a member from a note. We first need the object. *p is the "walk to the address" step.
PICTURE: the arrow from p is followed, and a spotlight lands on block a — we are now "holding" the object, exactly as if we had written a.

- — "go to where
ppoints." - — the address it reads and travels to.
- — you arrive at the exact same block as Step 2. From here you are in Step 3's situation again.
Step 6 — Now just dot it: (*p).m
WHAT: we combine the two moves — follow the note (*p), then walk the offset (.y).
WHY: the parentheses are required because . grabs tighter than *. Without them, *p.y would parse as *(p.y) — "grab member y of p (a pointer has no members!) then dereference" — which is wrong and won't compile.
PICTURE: two hops shown in sequence: hop A (*p) the long jump to block a; hop B (.y) the small jump to y.

- — the object, parentheses forcing "dereference first."
- — the ordinary base+offset walk from Step 3.
- — the exact box arithmetic that results: address .
Step 7 — The arrow is pure shorthand for those two hops
WHAT: we replace the clumsy (*p).y with p->y. They compile to the same box arithmetic: .
WHY: pointers-to-structs appear constantly (every linked-list node is reached this way — see Linked Lists). Typing (*p).m everywhere is noisy and error-prone, so C bakes in the arrow.
PICTURE: the two-hop path of Step 6, collapsed into a single labelled arrow ->, both landing on the same byte .

- — the shortcut; reads "the
yof whatppoints to." - — its exact meaning, no more, no less.
- — the single physical box both expressions end on.
The one-picture summary

This final picture stacks the whole journey: memory as boxes → a struct is an ordered block → dot = base+offset → a pointer is a box holding the base → dereference follows it → dot again → arrow collapses the two hops into one symbol. Read it top to bottom and you have re-derived -> from raw bytes.
Recall Feynman retelling — the whole walkthrough in plain words
Picture a street of numbered mailboxes. Your struct a is two adjacent mailboxes: box 1000 holds x, box 1004 holds y. When you write a.y, the compiler doesn't hunt for "y" — it just knows "y sits 4 boxes past the start," so it goes to . That's the dot: start address plus a fixed offset.
Now a friend hands you a sticky note that reads "1000." That note is the pointer p; it lives in its own faraway box and just stores the number 1000. You can't grab y from a sticky note. So you first walk to box 1000 — that walking is the star, *p. Once you're standing on the struct, grabbing y is the same old +4 dot: (*p).y. Two moves: walk, then step. Because you'd do this a thousand times in real code, C lets you write it as one arrow — p->y — which means exactly "walk to it, then step to y." Same mailbox, same value, just fewer keystrokes and no parenthesis traps.
Recall
p->m is shorthand for which expression? ::: (*p).m
Why must (*p).m keep its parentheses? ::: . binds tighter than *, so *p.m would parse as *(p.m).
What does the dot . compile down to, physically? ::: read at (base address of the object + fixed offset of the member).
What does a pointer variable actually store? ::: an address — the number of another box, not the object itself.
What single step does *p perform before the member is reachable? ::: it follows the stored address to stand on the object.
Connections
- Structures — parent topic — this page is the visual derivation of its
->rule. - Pointers in C — Steps 4–5 (address stored in a box, dereferencing) are pointer fundamentals.
- Linked Lists — the arrow's real home: every node reached via
node->next. - Memory Alignment & Padding — why offsets aren't always the naïve sum (Step 3's "+4" can jump).
- Passing Structures to Functions — why you pass
&aand reach in with->.