I would use more visual examples that target conceptual knowledge first. I would do a TON of memory diagrams with code tracing. Then, introduce the idea of pointers as memory addresses like you do in your example.
I am a big fan of Nick Parlante's pointer materials. I know they are vintage at this point, but I haven't found anything better at explaining pointers conceptually or at targeting common pointer errors like dereferencing pointers that don't point to anything (seg fault!).
Binky Pointer Fun video
This claymation video (Original webpage, YouTube version in C) is hilarious and a crowd-pleaser, but also explains pointers conceptually---without talking about memory addresses explicitly. For that reason, it has versions in C, C++, Java, and more.
Three Pointer Rules
This is my version of Nick Parlante's Three Pointer Rules. Definitely see the Pointer Basics document for full details and the original rules.
1) Drawing Pointers and Pointees
Draw a pointer as a box. [C-specific modification] Before it points to anything, that box has a "?" in it.
int *x;
When you allocate memory (make a pointee), draw it in the heap. Setting that pointee equal to x makes an arrow go from the pointer to the pointee.
x = malloc(sizeof(int));

Dereferencing changes the pointee---the thing the pointer points to (see next rule)
*x = 42;

2) Dereferencing
The dereference operation starts at the pointer and follows its arrow
over to access its pointee. Important: The dereference operation on a
pointer only works if the pointer has a pointee -- the pointee must be
allocated and the pointer must be set to point to it. You will save students so many seg faults if you emphasize this!
3) Pointer Assignment
Pointer assignment between two pointers makes them point to the same
pointee. So the assignment y = x; makes y point to the same pointee as
x.
Connecting Pointer rules to code
To drive it home, walk through code line-by-line while drawing diagrams. Have students work on their own code tracing and diagrams in groups.
Note that Nick Parlante emphasizes dynamically allocated memory in his code examples (malloc
), and avoids the "address of" operator &
. I think this is a smart choice because it's what is most common in real code. But of course &
can be introduced in this method.
Here are some simple sample code snippets to start the walk-throughs and memory drawings:
// non-pointer code
double num;
num = 42.7;
printf(num);
// pointer code
double* num_ptr;;
num_ptr = malloc(sizeof(double));
*num_ptr = 42.7;
printf(*num_ptr);
// Binky's code
int* x;
int* y;
x = malloc(sizeof(int));
*x = 42;
*y = 13; // yes it crashes, but that's the point >:)
y = x;
*y = 13;