C++ Reference vs Pointer: What’s the Difference?
Both references and pointers let you work with variables indirectly — but they have different rules, different syntax, and different appropriate uses. If you already understand pointers, references are easy to learn by comparison.
The Basic Syntax
Pointer:
int x = 10;
int* p = &x; // p holds the address of x
*p = 20; // dereference to change x
cout << x; // 20
Reference:
int x = 10;
int& ref = x; // ref is an alias for x
ref = 20; // changes x directly — no dereference needed
cout << x; // 20
The reference ref and the variable x are the same thing — just different names for the same memory location.
The Key Differences
1. Initialization
References must be initialized when created:
int* p; // OK — uninitialized pointer (dangerous but legal)
int& ref; // Error — references must be initialized
int& ref = x; // Must bind immediately
2. Nullability
Pointers can be null; references cannot:
int* p = nullptr; // Valid — a null pointer
int& ref = ???; // No such thing as a null reference
3. Reassignment
Pointers can point to different objects; references cannot be rebound:
int a = 1, b = 2;
int* p = &a;
p = &b; // p now points to b — fine
int& ref = a;
ref = b; // This does NOT rebind ref — it assigns b's VALUE to a
// ref still refers to a, and a is now 2
4. Syntax for Access
Pointers require * to dereference and -> for members; references use the same syntax as the original variable:
struct Point { int x, y; };
Point pt = {3, 4};
// Pointer
Point* pp = &pt;
cout << pp->x; // Arrow operator for member access
cout << (*pp).x; // Equivalent — dereference then member access
// Reference
Point& pr = pt;
cout << pr.x; // Same syntax as pt.x — no dereferencing needed
5. Memory Address
References don’t have their own address (from a conceptual standpoint). Getting the address of a reference gives the address of the original object:
int x = 42;
int& ref = x;
cout << &x; // Same address
cout << &ref; // Same address — they're the same object
Using References in Function Parameters
This is the most common use of references — passing by reference to avoid copying and to allow modification:
// Pass by value: function gets a copy (changes don't affect original)
void doubleVal(int x) {
x *= 2;
}
// Pass by pointer: works, but caller must pass &variable, function must use *
void doublePtr(int* x) {
*x *= 2;
}
// Pass by reference: cleanest — no pointer syntax, can't be null
void doubleRef(int& x) {
x *= 2;
}
int n = 5;
doubleVal(n); // n is still 5
doublePtr(&n); // n is now 10
doubleRef(n); // n is now 10 (after resetting)
References are preferred for function parameters because:
- The call site looks like passing by value (
doubleRef(n)notdoubleRef(&n)) - No null check needed
- No dereferencing needed inside the function
const References
One of the most important patterns in C++: pass by const reference to avoid copying without allowing modification:
// Passes a copy — slow for large objects
void print(string s) { cout << s; }
// Passes by const reference — no copy, no modification allowed
void print(const string& s) { cout << s; }
string bigString(1000000, 'x'); // 1 million characters
print(bigString); // const ref version: no copy made
const string& is the standard way to pass strings, vectors, and other large objects to functions that don’t need to modify them.
When to Use Each
Use references when:
- Passing parameters to functions (prefer
const T&for read-only,T&for writable) - Returning values (especially from operator overloads like
operator[]) - Creating a simple alias for a variable
- You need something that cannot be null
Use pointers when:
- The value might not exist (
nullptras a sentinel) - You need to reassign to point to different objects
- You’re managing dynamic memory (
new/delete) - You need pointer arithmetic (e.g., iterating over an array)
- Interfacing with C APIs that use pointers
Practical Example: Swap Function
// With pointers (C style)
void swapPtr(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
swapPtr(&x, &y); // Must pass addresses
// With references (C++ style)
void swapRef(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
swapRef(x, y); // Pass variables directly — cleaner
Summary Table
| Feature | Pointer | Reference |
|---|---|---|
| Syntax | int* p = &x; | int& r = x; |
| Can be null | Yes (nullptr) | No |
| Initialization required | No | Yes |
| Can be reassigned | Yes | No |
| Access member | p->member | r.member |
| Get address | p (is the address) | &r (same as original) |
| Arithmetic | Yes | No |
| Use for functions | void f(int* p) | void f(int& r) |
Related Articles
- How to Use Pointers in C++ — pointer fundamentals from scratch
- C++ Pass by Value and Reference — detailed guide on passing parameters
- C++ Functions Tutorial — using references in function design
- Smart Pointers in C++ — modern alternative to raw pointers
Take Your C++ Further
If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.