Back to Articles

Pointers and References in C++

SkillStream Editorial
August 27, 2026

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.

cpp
int 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.

cpp
int 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 (nullptr in 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.
cpp
int* 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.

cpp
int 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

PointerReference
Can be nullYesNo — must always refer to something
Can be reassigned to refer elsewhereYesNo — bound permanently at creation
Needs dereferencing to access the valueYes (*ptr)No — used like a normal variable
Supports arithmeticYesNo

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

cpp
void 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.