5.1.22 · D2C Programming

Visual walkthrough — Structures — declaration, accessing members (. and - - )

2,096 words10 min readBack to topic

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.

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

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.

Figure — Structures — declaration, accessing members (. and - - )
  • — the address where a begins; 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.

Figure — Structures — declaration, accessing members (. and - - )
  • — 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.

Figure — Structures — declaration, accessing members (. and - - )
  • — 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 a begins.

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.

Figure — Structures — declaration, accessing members (. and - - )
  • — "go to where p points."
  • — 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.

Figure — Structures — declaration, accessing members (. and - - )
  • — 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 .

Figure — Structures — declaration, accessing members (. and - - )
  • — the shortcut; reads "the y of what p points to."
  • — its exact meaning, no more, no less.
  • — the single physical box both expressions end on.

The one-picture summary

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

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 &a and reach in with ->.