Menu

C++ References vs Pointers: When to Use Each

A practical comparison of references and pointers in C++ - what they share, where they differ (rebinding, null, arithmetic), and a clear rule for which one to reach for in everyday code.

This page includes runnable editors - edit, run, and see output instantly.

Two Ways to Refer to Something

You already know pointers - variables that store an address and let you reach the object living there. References are the other tool C++ gives you for indirectly working with an existing object. They overlap enough that beginners often can't tell which to pick, so this page lines them up side by side.

The short version: a reference is an alias. Once int& r = x; runs, r is x - same object, different name. A pointer is a separate object that happens to hold the address of another. That single difference drives everything else.

A Reference Is an Alias

A reference must be tied to an object the moment it's created, and from then on every use of the reference touches the original.

Notice there is no * to dereference and no & to "take the address" at the point of use - you read and write alias exactly like a plain int. The & in int& alias is part of the type, not the address-of operator.

Where They Differ

The behaviors below are the whole reason both tools exist. This is the table to memorize.

//                      reference            pointer
// must be initialized? yes                  no (but should be)
// can be null?         no                   yes (nullptr)
// can be reseated?     no                   yes
// pointer arithmetic?  no                   yes
// syntax to use it     just the name        *p  or  p->member
// taking address       &ref == &original    &p is the pointer's own address

Two of these trip people up the most. First, a reference can never be reseated - assigning to it copies a value into the referred object, it does not point the reference at something new.

A pointer, by contrast, is free to point somewhere else at any time:

Second, a reference can never legally be null, while a pointer can. That makes "no value" representable with a pointer but not with a reference - a property you'll lean on constantly.

Choosing in Function Parameters

This is where the choice shows up most. When a function needs to read or modify a caller's object, both work, but they signal different intent.

The reference version (addTax(cart)) is impossible to call with "nothing," so inside the function you never check for null - the object is guaranteed to be there. The pointer version (applyDiscount(&cart)) advertises at the call site, via the &, that the argument might be changed, and it lets the caller pass nullptr to mean "not applicable." Pick the one whose guarantee matches your function.

For read-only parameters of large types, the idiomatic choice is const T& - it avoids a copy and promises not to modify. See function parameters for more on pass-by-value vs pass-by-reference.

A Simple Rule of Thumb

When you're unsure, default to a reference and only upgrade to a pointer when you need a capability a reference lacks:

  • Use a reference when the object always exists and its identity never changes - the common case for function parameters and aliases.
  • Use a pointer when any of these is true:
    • "Nothing" is a valid state (optional argument, a search that may find no match) - a pointer can be nullptr.
    • You need to point at different objects over time - a pointer can be reseated.
    • You're managing heap memory you'll delete (or, better, a smart pointer), or walking an array with pointer arithmetic.

If none of those apply, a reference is the cleaner, safer choice because the compiler enforces "always valid, never reseated" for you.

Common Mistakes to Avoid

  • Expecting ref = other to reseat. It assigns a value into the referred object instead. References are bound for life; if you need reseating, use a pointer.
  • Returning a reference (or pointer) to a local. int& f() { int x = 5; return x; } returns a dangling reference - x dies when f returns and using the result is undefined behavior. The same trap hits pointers (return &x;).
  • Forging a "null reference." Writing int& r = *p; when p is nullptr is undefined behavior the instant you dereference, not a safe "empty" reference. Express optionality with a pointer or std::optional instead.
  • Reaching for a pointer out of habit. If the argument always exists and you'll never reseat, a reference removes a whole class of null checks and crashes. Don't pay for capabilities you don't use.

Next: Dynamic Memory

So far every object you've referenced or pointed at was created automatically on the stack. The next page, dynamic memory, covers new and delete - asking the operating system for memory at runtime, why pointers (not references) own it, and how forgetting to free it causes leaks.

Frequently Asked Questions

What is the difference between a reference and a pointer in C++?

A reference is an alias for an existing object: it must be initialized, can never be null, and can never be made to refer to a different object afterward. A pointer is a separate variable holding an address: it can be null, can be reseated to point elsewhere, and supports pointer arithmetic. Use & syntax with references and */-> with pointers.

When should I use a pointer instead of a reference in C++?

Use a pointer when "nothing" is a valid state (an optional argument, a not-found result), when you need to reseat it to point at different objects over time, or when you own heap memory you'll delete. Use a reference when the thing always exists and never changes identity - which covers most function parameters.

Can a reference be null in C++?

No. A valid reference always refers to a real object, so you never check it for null. If you create a reference from a dereferenced null pointer (int& r = *p; where p is null) you get undefined behavior, not a null reference. When you need to express "maybe nothing," use a pointer or std::optional.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED