Pointers References

By the end of this lesson you'll be able to alias a variable with a reference, store and follow a memory address with a pointer, reach into a struct with -> , and manage heap memory the modern way — with smart pointers that clean up after themselves so your programs never leak.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free C++ course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Think of your computer's memory as a street of houses, each with a numbered address. A variable is a house with stuff inside it.

A reference is a nickname for one specific house — "Mum's place" always means the same house, you can't later point it at a different one, and there's no such thing as a nickname for a house that doesn't exist. That's why a reference must be initialised and can never be null.

A pointer is a sticky note with a house address written on it. You can read the address, follow it to visit the house ( dereference ), scribble a new address to point somewhere else, or leave it blank ( nullptr ). More flexible than a nickname — and easier to get wrong, because a blank or stale note leads nowhere good.

1. References — a Second Name for a Variable

A reference is an alias: a second name for a variable that already exists. You write it with & in the type — int& alias = score; — and from then on alias and score are the exact same box in memory. Two rules make references safe: a reference must be initialised the moment you declare it, and it can never be re-bound to a different variable afterwards. Read this, run it, and watch how changing the alias changes the original.

2. Pointers — Storing an Address

A pointer is a variable that stores a memory address instead of a plain value. Three pieces of syntax do all the work: &x reads "the address of x", int* p declares "p is a pointer to an int", and *p reads "the value p points at" (called dereferencing ). A pointer that points at nothing holds nullptr — always safe to test before you follow it.

Your turn. The program below is almost complete — fill in the three blanks marked ___ using the hints in the comments, then run it and check your output against the expected lines.

They both let you work with another variable indirectly, so when do you use which? A reference is the safer default — use it when the target always exists and never needs to change. A pointer earns its keep when you need "maybe nothing" ( nullptr ) or "different things over time".

3. Pointers to Structs — the -> Operator

When you have a pointer to a struct, reaching a member is so common that C++ gives it a shortcut. Instead of writing (*p).name — dereference first, then use the dot — you write p->name . The arrow does both steps at once and reads much more cleanly. You'll use -> constantly, including with the smart pointers coming up next.

4. Dynamic Memory — new , delete , and Why It's Risky

So far every variable has lived on the stack and vanished automatically at the end of its scope. Sometimes you need memory that outlives the current function — that's the heap , and you ask for it with new . The price: every new needs a matching delete , and every new[] needs a matching delete[] . Miss one and you leak memory; get the pairing wrong and you crash. This is the manual way — study it, then meet the tool that makes it obsolete.

5. Smart Pointers — the Modern Default

A smart pointer owns heap memory and frees it automatically when it goes out of scope — no delete to remember, ever. They live in <memory> . Use std::unique_ptr for single ownership and create it with std::make_unique ; it's the lightweight default you should reach for first. Use std::shared_ptr (made with std::make_shared ) only when several parts of your program must share one object — it counts owners and frees the object when the last one leaves. You still use -> to reach members, exactly like a raw pointer.

Now you try. Fill in the two blanks below to create a std::unique_ptr and reach a member through it. There is no delete to write — that's the whole point.

No blanks this time — just a brief and an outline to keep you on track. Build it, run it, and check your output against the example in the comments. Owning the Pet with a unique_ptr means you never write a single delete .

Practice quiz

What is a reference (int& alias = score;)?

  • A copy of score stored elsewhere
  • A pointer to score that can be re-pointed
  • A second name (alias) for the same variable in memory
  • A null placeholder until assigned

Answer: A second name (alias) for the same variable in memory. A reference is an alias: alias and score are the same box in memory, so changing one changes both.

Which two rules make references safe?

  • They must be initialised when declared and can never be re-bound
  • They can be null and must be const
  • They live on the heap and are freed automatically
  • They can only refer to integers

Answer: They must be initialised when declared and can never be re-bound. A reference must be initialised at declaration and can never be reseated to a different variable afterwards.

What does the & operator do in the expression &age?

  • Dereferences age
  • Declares a reference
  • Bitwise-ANDs age with itself
  • Gives the address in memory where age lives

Answer: Gives the address in memory where age lives. In an expression, &age is 'address-of' — it yields the memory address of age.

Given int age = 25; int* ptr = &age; *ptr = 30;, what is age afterwards?

  • 25
  • 30
  • 0
  • Undefined

Answer: 30. Writing through the pointer (*ptr = 30) changes the original variable, so age becomes 30.

What does p->name mean for a pointer p to a struct?

  • Shorthand for (*p).name — dereference then access the member
  • The address of name
  • A new copy of name
  • It only works on smart pointers

Answer: Shorthand for (*p).name — dereference then access the member. The arrow -> is sugar for (*p).name: dereference the pointer, then access the member.

When should you prefer a pointer over a reference?

  • Always — pointers are faster
  • When the target always exists and never changes
  • When you need 'might point at nothing' (nullptr) or 'can point at different things over time'
  • Only inside classes

Answer: When you need 'might point at nothing' (nullptr) or 'can point at different things over time'. Use a reference by default; reach for a pointer when you genuinely need null or re-pointing.

What must every new int(...) be paired with, and every new int[...]?

  • delete for both

A single new pairs with delete; an array new[] pairs with delete[]. Mixing them is undefined behaviour.

Which smart pointer has exactly one owner and frees the object when it goes out of scope?

  • std::shared_ptr
  • std::weak_ptr
  • a raw pointer
  • std::unique_ptr

Answer: std::unique_ptr. unique_ptr is single-ownership and the lightweight default; it deletes automatically with no manual delete.

In the smart-pointer example, after shared_ptr<Player> b = a; inside a block, what does a.use_count() report, and after the block?

  • 1 inside, 0 after
  • 2 inside, 1 after
  • 2 inside, 2 after
  • It always stays 1

Answer: 2 inside, 1 after. Copying the shared_ptr raises the count to 2; when b leaves scope it drops back to 1, and the object lives on with a.

Why prefer nullptr over NULL or 0 for a null pointer?

  • nullptr is faster at run time
  • NULL and 0 are compile errors in C++
  • nullptr is a proper null-pointer type, so the compiler distinguishes 'no pointer' from the integer 0
  • nullptr automatically frees memory

Answer: nullptr is a proper null-pointer type, so the compiler distinguishes 'no pointer' from the integer 0. nullptr (C++11) has a real null-pointer type, avoiding the overload-resolution confusion of the old integer 0/NULL.