References vs pointers — syntax & mental model
Scenario
You pass a variable to a function intending to modify it, but the original variable remains unchanged. Or worse, you return a reference to a local variable and your program segfaults.
How do you safely give a function access to modify your data without copying it?
Mental model
A pointer is a variable that holds a memory address. It can point to nothing (null) or change what it points to. A reference is just a permanent alias (a second name) for an existing object. It cannot be null and cannot be reassigned.
In C++, references (`&`) and pointers (`*`) both allow you to refer to an object indirectly, avoiding expensive copies. However, a reference is an alias bound at initialization—it acts exactly like the original object. A pointer is a distinct object holding an address. Because references cannot be null and cannot be reseated (rebound to another object), they are safer and preferred for function parameters. Pointers are necessary for dynamic memory, arrays, and situations where 'no object' (nullptr) is a valid state.
Explanation
A pointer is a memory address. Because it's a distinct variable, you can reassign it to point somewhere else, and you can set it to `nullptr` to indicate it points nowhere. You must explicitly dereference it with `*` or `->` to access the value.
A reference is an alias. It must be initialized when it is declared, and it cannot be made to alias a different object later. Any operation on the reference is an operation on the aliased object. Under the hood, compilers often implement references using pointers, but the language hides the indirection, making the syntax cleaner (using `.` instead of `->`).
The most dangerous pitfall with both is dangling: referring to memory that has been freed or has gone out of scope. Never return a reference or a pointer to a local variable allocated on the stack, because the variable is destroyed when the function returns, leaving the reference pointing to invalid memory (Undefined Behaviour).
Code examples
Syntax comparison and reassignment
int a = 10;
int b = 20;
// Pointer
int* ptr = &a; // ptr holds the address of a
*ptr = 11; // a is now 11
ptr = &b; // ptr now points to b
ptr = nullptr; // ptr points to nothing
// Reference
int& ref = a; // ref is an alias for a
ref = 12; // a is now 12
ref = b; // This DOES NOT rebind ref. It assigns the value of b (20) to a.
Notice that `ref = b` assigns the *value* of `b` into the variable `a` because `ref` is permanently aliased to `a`. You cannot change which object a reference aliases after initialization.
The bug: Returning a reference to a local variable
#include <iostream>
int& getBadReference() {
int local_val = 42;
return local_val; // ERROR: returning reference to local variable
}
int main() {
int& ref = getBadReference();
std::cout << ref << '\n'; // Undefined behaviour! The memory for local_val is gone.
return 0;
}
When `getBadReference` returns, its stack frame is destroyed. `local_val` no longer exists. `ref` in `main` is now a 'dangling reference'. Accessing it is undefined behaviour.
Key points
- References are permanent aliases: Must be initialized upon declaration and cannot be reseated or null.
- Never return local references: Variables on the stack are destroyed when the function returns, resulting in dangling references.
Common mistakes
- Thinking references can be reseated: Assigning to a reference does not change what it points to; it overwrites the value of the object it already aliases.
- Checking if a reference is null: References cannot be null by definition. (If you manage to create a null reference through pointer casting magic, your program is already in Undefined Behaviour).
Recall questions
- Can a reference be reassigned to alias a different object?
- Why is returning a reference to a local variable dangerous?
- When should you use a pointer instead of a reference?
Questions & answers
In a C++ code review, you see a function signature `void processNode(Node* n)`. The function body immediately accesses `n->value`. What is the issue?
Since it takes a pointer, `n` could be `nullptr`. The code dereferences it without checking, risking a segmentation fault. It should either check for `nullptr` first, or be changed to take a reference `void processNode(Node& n)` to guarantee a valid object is passed.
Approach: Identify that pointers allow nulls (requiring safety checks), whereas references guarantee validity.
A candidate claims that references are just syntactic sugar and are implemented exactly like pointers under the hood, so they have the same performance and memory characteristics. Is this true?
Yes, compilers typically implement references as pointers. They have the same performance and memory layout. The difference is purely at the language level: references enforce initialization and prevent reseating and null values.
Approach: Distinguish between language semantics (safety rules) and compiler implementation (underlying memory).
Continue learning
Previous: Data types, I/O (cin/cout) & fast I/O tricks
Next: const correctness — const refs, const methods & when to use