Pointers and References in C++
A practical guide to pointers and references in C++ — what they actually are, how they differ, common pitfalls like dangling pointers, and when to reach for each one.
Pointers are the single biggest reason C++ has a reputation for being harder to learn than most other languages — and also the reason it gives you a level of control that higher-level languages simply don't offer. Understanding them properly is the difference between fighting the language and using it well.
What Is a Pointer?
Every variable lives at a specific address in memory. A pointer is a variable that stores that address rather than the value itself.
cppint age = 25; int* ptr = &age; // ptr now holds the memory address of age std::cout << age; // 25 std::cout << ptr; // some memory address, e.g. 0x7ffee... std::cout << *ptr; // 25 — dereferencing ptr gives the value it points to
Two operators do the work here:
&— the "address-of" operator, gets the memory address of a variable*— the "dereference" operator, gets the value stored at the address a pointer holds
Pointer Arithmetic
Because pointers hold addresses, you can move them — particularly useful with arrays, where elements sit next to each other in memory.
cppint numbers[] = {10, 20, 30}; int* p = numbers; // points to numbers[0] std::cout << *p; // 10 p++; // move to the next int-sized address std::cout << *p; // 20
Null and Dangling Pointers — The Two Classic Bugs
- Null pointer. A pointer that intentionally points to nothing (
nullptrin modern C++). Dereferencing it crashes the program — always check before use. - Dangling pointer. A pointer that still holds an address after the memory there has been freed or gone out of scope. Using it leads to undefined behavior — sometimes it silently works, sometimes it corrupts data, sometimes it crashes, which makes it especially dangerous to debug.
cppint* danger() { int local = 5; return &local; // BUG: local is destroyed when the function returns }
References: A Safer Alternative
A reference is an alias for an existing variable — another name for the same memory, rather than a separate variable holding an address.
cppint score = 90; int& refScore = score; // refScore is another name for score refScore = 95; std::cout << score; // 95 — changing refScore changed score directly
Pointers vs. References: When to Use Which
| Pointer | Reference | |
|---|---|---|
| Can be null | Yes | No — must always refer to something |
| Can be reassigned to refer elsewhere | Yes | No — bound permanently at creation |
| Needs dereferencing to access the value | Yes (*ptr) | No — used like a normal variable |
| Supports arithmetic | Yes | No |
The common guidance: use references when a valid value is guaranteed and you don't need to reassign what's being referred to — most commonly for function parameters, to avoid copying large objects. Use pointers when you need the option of "no value" (null), when you need to reassign what's being pointed to, or when working with dynamic memory.
Pointers and Function Parameters
cppvoid increment(int* value) { (*value)++; } void incrementRef(int& value) { value++; } int x = 5; increment(&x); // must explicitly pass the address incrementRef(x); // just pass the variable — the reference handles it
Both modify the original variable rather than a copy — but the reference version is cleaner to call and harder to misuse.
The Takeaway
Pointers and references both let you work with a variable's actual memory instead of a copy of its value — the difference is safety versus flexibility. References are the safer, more restricted tool for everyday use; pointers give you the full power (and full responsibility) of direct memory control, which is exactly why modern C++ leans on references and smart pointers wherever raw pointers aren't strictly necessary.


