Source: CS 225 Data Structures, UIUC
Tags: pointers, references, pass by value, pass by reference, pass by pointer, indirection, dereferencing, memory address, dynamic allocation, new, delete, C++ memory model
Difficulty: Intermediate Prerequisites: Basic C++ syntax (variables, functions). Understanding of the stack and heap memory regions is helpful but not required.
Pointers and references are how C++ lets you work with memory directly. Every linked data structure you build in CS 225 (linked lists, trees, graphs) depends on pointers to connect nodes. Understanding the difference between passing arguments by value, by reference, and by pointer is essential for writing functions that modify data efficiently and correctly. If you are shaky on pointers, most of the course's implementation work will feel mysterious. This topic underpins everything from the Rule of Three to tree traversal.
Pointers store memory addresses and allow dynamic allocation; references are aliases that refer to the same memory as the original variable. Passing by value copies data (safe but potentially expensive), passing by reference shares the original (efficient, allows modification), and passing by pointer also shares the original but requires explicit dereferencing. Indirection, the ability to access data through a pointer, is the mechanism that makes linked data structures possible.
Pointer
A variable that stores the memory address of another variable. Declared with (e.g., int p). Pointers enable dynamic memory allocation (new/delete), building linked data structures, and accessing data indirectly.
In simple terms: "A pointer is an arrow that says 'the data you want is over there, at this address.'"
Reference
An alias for an existing variable. Declared with & (e.g., int& r = x). A reference shares the same memory location as the variable it refers to. Unlike pointers, references cannot be null, cannot be reassigned to refer to a different variable after initialisation, and do not need dereferencing syntax.
In simple terms: "A reference is a second name for the same box in memory."
Pass by value
A method of passing function arguments where a copy of the value is made and given to the function. Changes inside the function do not affect the original variable. Safe, but copying large objects is expensive.
Pass by reference
A method of passing function arguments where the function receives a reference (alias) to the original variable. Changes inside the function modify the original. No copying overhead. Syntax: void f(int& x).
Pass by pointer
A method of passing function arguments where the function receives the address of the variable. The pointer must be explicitly dereferenced (*p) to access or modify the value. Similar effect to pass by reference, but with different syntax and the possibility of a null pointer. Syntax: void f(int* p).
Indirection (dereferencing)
The act of accessing the value that a pointer points to, using the * operator. Indirection lets you manipulate data stored at an arbitrary memory address, which is how linked data structures traverse from node to node.
In simple terms: "Following the arrow to get to the actual data."
Dynamic allocation
Allocating memory on the heap at runtime using new. The memory persists until explicitly freed with delete. This is how linked-list nodes and tree nodes are created: each node is individually allocated on the heap and connected via pointers.
Null pointer
A pointer that does not point to any valid memory. In C++, represented by nullptr (or NULL in older code). Dereferencing a null pointer is undefined behaviour and typically causes a crash (segfault).
Both let you work with data without copying it
A pointer can be null; a reference cannot
A pointer can be reassigned to point to a different variable; a reference is bound at initialisation and cannot be rebound
Pointers require explicit dereferencing (*p); references are used with normal variable syntax
In data structures, pointers are the standard choice for node links because they can be null (representing the absence of a child or successor)
By value: void f(int x) – the function gets its own copy. Safe, but copying large objects (e.g., a vector with 10,000 elements) is expensive.
By reference: void f(int& x) – the function operates on the original. Efficient, and changes persist after the function returns. Use const int& when you want efficiency without allowing modification.
By pointer: void f(int* x) – the function receives an address. Must dereference to use the value. Can pass nullptr to indicate "no value," which is not possible with references.
Every node in a linked list or tree contains a pointer to the next node (or child nodes)
Traversing the structure means repeatedly following pointers: indirection
When a pointer is NULL/nullptr, you have reached the end of the chain (or an absent child in a tree)
Indirection is what makes dynamic, heap-allocated structures flexible: nodes do not need to be contiguous in memory
new allocates on the heap and returns a pointer
delete frees heap memory; failing to call it causes a memory leak
Every new must have a matching delete somewhere in your code
This is the core reason the Rule of Three exists: when an object owns heap memory, you need to handle copying and destruction carefully
Pointer declaration and dereferencing:
int x = 42;
int* p = &x; // p stores the address of x
*p = 99; // dereferences p, sets x to 99
Reference binding:
int x = 42;
int& r = x; // r is an alias for x
r = 99; // x is now 99
Linked list traversal via indirection:
Node* current = head;
while (current != nullptr) {
// process current->data
current = current->next; // follow the pointer
}
Pointers and dynamic allocation are how operating systems manage memory for every running process. Device drivers, embedded systems, and game engines rely on pointer arithmetic and manual memory management for performance-critical code. The concept of indirection extends beyond C++ into database indexing (a B-tree node holds pointers to child nodes on disk), networking (packets contain addresses), and even web URLs (a URL is a pointer to a resource).
Students often think references and pointers are the same thing. They serve similar purposes, but references cannot be null, cannot be rebound, and do not require dereferencing syntax. Pointers are more flexible but more error-prone.
"Pass by reference" and "pass by pointer" are sometimes treated as identical. The syntax and semantics differ: pass by reference cannot receive nullptr, requires no dereferencing in the function body, and cannot be reassigned to point elsewhere.
Students sometimes believe that new memory is automatically freed when the function returns. It is not. Heap memory persists until explicitly freed with delete. This is a common source of memory leaks.
A null pointer is not the same as an uninitialised pointer. A null pointer has a well-defined value (nullptr); an uninitialised pointer holds garbage and dereferencing it is unpredictable.
⚠️ Be able to trace pointer operations: given a sequence of assignments and dereferences, state the final values of all variables.
⚠️ Know the difference between pass by value, pass by reference, and pass by pointer, and state when each is appropriate.
⚠️ Understand why linked data structures use pointers (and specifically why nullptr is meaningful as a sentinel).
⚠️ Recognise memory leaks: identify code where new has no matching delete.
True or False: A reference in C++ can be reassigned to refer to a different variable after initialisation.
Fill in the blank: Dereferencing a null pointer causes ______.
True or False: Pass by value allows the function to modify the caller's original variable.
Fill in the blank: Every call to new must have a corresponding call to ______.
True or False: A pointer can be nullptr, but a reference cannot.
Q: What is the difference between a pointer and a reference in C++?
A: A pointer stores a memory address, can be null, can be reassigned, and requires explicit dereferencing. A reference is an alias bound at initialisation, cannot be null, cannot be rebound, and uses normal variable syntax.
Q: When would you choose pass by pointer over pass by reference?
A: When you need the ability to pass "nothing" (a null pointer to indicate the absence of a value), or when you want to make it visually explicit at the call site that the function may modify the argument (since you must write &x when calling).
Q: What happens if you forget to call delete on memory allocated with new?
A: The memory is leaked. It remains allocated on the heap for the lifetime of the program but is no longer accessible. Over time, accumulated leaks can exhaust available memory.
Q: Why do linked lists and trees use pointers rather than references for node connections?
A: Because a node may have no successor (end of a list) or no child (leaf in a tree), which is represented by nullptr. References cannot be null, so they cannot represent the absence of a connection.
Pointers connect directly to the Rule of Three: if your class stores pointers to heap memory, you must manage copying and destruction manually.
Indirection is the mechanism behind every linked data structure in the course: linked lists, stacks, queues (when linked), binary trees, and graphs.
Understanding pass-by-reference is essential when writing recursive tree functions, where you may pass a pointer by reference to modify the tree structure itself.
pointer, reference, alias, pass by value, pass by reference, pass by pointer, dereference, indirection, memory address, dynamic allocation, heap, stack memory, new, delete, nullptr, NULL, memory leak, segfault, undefined behaviour, node pointer, linked data structure, CS 225 UIUC, C++ memory management