Pointers, this, and Dynamic Memory
"Therefore, since we are surrounded by so great a cloud of witnesses..." — Hebrews 12:1
Why This Matters
For ten chapters you have used variables that the compiler put somewhere in memory without telling you where. This chapter takes the hood off.
A pointer is a variable whose value is the memory address of another variable. With pointers you can refer to things without copying, build data structures whose size is decided at runtime, and access the same data from multiple places.
For the apologetics theme: the chain of historical Christian witness. From the Apostles, through the Apostolic Fathers (Clement, Ignatius, Polycarp), through the early Church Fathers, the Reformers, the modern Church — each generation pointed back to the one before. A linked list, naturally. (Note: this is the historical continuity of public testimony, not Roman-style apostolic succession — that's a different doctrinal claim.)
Three Pointer Operations
&x— "address of x" (produces anint*whenxis an int)*p— "the int that p points to" (dereference)p->field— when p points to a struct/class, access a field
int x = 42;
int* p = &x; // p holds the address of x
cout << *p; // 42 — dereference
*p = 100; // modify x through p
cout << x; // 100 new and delete
For memory whose lifetime you control yourself:
Witness* w = new Witness;
w->name = "Polycarp";
w->century = 2;
// ... use w ...
delete w; // free the memory when done
w = nullptr; // defensive: future *w would crash, but at least we know why Every new must have a matching delete on every
code path. Forget the delete and you've leaked memory. Programs that leak
eventually run out. AddressSanitizer (compile with -fsanitize=address) will
catch leaks reliably.
Self-Referencing Structures (Linked Lists)
struct Witness {
string name;
int century;
Witness* next; // pointer to the next witness in the chain
};
Each witness knows who comes next. The last one has next = nullptr. To traverse:
Witness* current = head;
while (current != nullptr) {
cout << current->name << endl;
current = current->next;
} That loop is the canonical pattern. The widget above does exactly this on every render.
The Destructor's Job
~Chain() {
Node* current = head;
while (current != nullptr) {
Node* next = current->next; // save before delete
delete current;
current = next;
}
} Save next before delete. Forget to save and you've
just freed the node whose next pointer you were about to dereference. Classic
use-after-free bug.
Coach's Note — This is the technical hardest chapter in the course. Pointers, manual memory management, linked structures are the bedrock of the language. If Chapter 11 takes you twice as long as the others, that's normal. Slow down. Draw the lists on paper. Java's garbage collector (Chapter 13) will feel like a gift after this.
This Week's Project
You're ready for Project 11: Chain of Witnesses. Build a singly linked list of real historical Christian witnesses with add/remove/print/destructor. Stress test with 100 nodes. Verify zero memory leaks via AddressSanitizer. Do not invent quotes — every witness must have a real, attributable testimony.
Pointers — Quick Check
int x = 42;
int* p = &x;
*p = 100;
cout << x; p holds the address of x. *p = 100 dereferences p (gets to the int it points to) and assigns 100. Since p points to x, this modifies x through the pointer. x is now 100. Node* current = head;
while (current != nullptr) {
delete current;
current = current->next;
} delete current, the memory at that location is no longer ours — reading current->next is undefined behavior. The fix is to save next before deleting:Node* next = current->next; delete current; current = next; delete at the end of a function that called new. What happens?-fsanitize=address (AddressSanitizer) to catch these in development.