Pointers vs References in C++: Key Differences Explained

Pointers vs References in C++: Key Differences Explained


If you are learning C++, one of the first questions you’ll face is the difference between pointers and references in C++. Both let you work with objects indirectly, but they serve different purposes and have different rules. In this beginner-friendly guide, you’ll learn what pointers and references are, how they differ, when to use each one, and the common mistakes to avoid. We’ll use simple, runnable examples and clear explanations suitable for students and new C++ learners.

Key takeaways: difference between pointers and references in C++
  • Pointers can be nullptr and can be reseated; references cannot.
  • References express a required, non-null alias; pointers often express “optional”.
  • References use normal object syntax (r.member); pointers need *p or p->.
  • Neither implies ownership; use smart pointers for that.

What is a pointer in C++?

Memory diagram of the difference between pointers and references in C++
Pointers store addresses; references alias the same object. Visual memory view.

A pointer is a variable that stores the memory address of another object. You can think of it as a “locator” that tells you where a value lives in memory. Because pointers hold addresses, they can be null (point to nothing), changed to point to something else, and even moved around using pointer arithmetic (mainly useful with arrays and low-level tasks).

Why use pointers?

  • To represent an optional object (use nullptr to mean “absent”).
  • To reassign which object is being accessed (reseat to another address).
  • For interoperability with C APIs, arrays, and low-level memory manipulation.

Basic pointer syntax:

  • Declare: T* p;
  • Point to an object: p = &obj;
  • Dereference to access the object: *p or p->member
  • Null pointer: T* p = nullptr; (since C++11)

What is a reference in C++?

Storyboard visualizing difference between pointers and references in C++: pointer arithmetic, null, fixed reference
Pointer can move and be null; a reference is bound to one object and cannot rebind.

A reference is an alias to an existing object. After you bind a reference to a variable, it becomes another name for that same variable. A reference must be initialized when created, and you cannot later reseat it to refer to a different object. In ordinary C++, a reference must be bound to a valid object; attempting to treat a null like an object and bind a reference to it leads to undefined behavior.

Why use references?

  • To express a required, non-null parameter in your function interface.
  • To avoid pointer syntax while still avoiding copying large objects.
  • To make code easier to read: accessing a reference looks like normal object access.

Basic reference syntax:

  • Declare and bind: T& r = obj;
  • Use like a normal variable: r accesses the original obj

Pointers vs References: quick comparison

Feature Pointer (T*) Reference (T&)
Can be null Yes (nullptr) Must refer to a valid object; “null reference” is undefined behavior
Can be reseated Yes (p = &other;) No; bound once at initialization
Syntax to access *p, p->member r, r.member
Default initialization Uninitialized until set; can be nullptr Must be initialized immediately
Pointer arithmetic Allowed (use carefully) Not applicable
Common use-cases Optional params, C interop, arrays/buffers Required params, clear aliasing, readability

Difference between pointer and reference in C++ with examples

This short program shows the core differences: dereferencing, reseating a pointer, and the fact that references remain bound to the original object.

Code
#class="cd-package">include <iostream>
class="cd-package">using class="cd-package">namespace std;

int main() {
    int x = 42;
    int y = 100;

    int* p = &x;   // pointer to x
    int& r = x;    // reference (alias) to x

    cout << "x=" << x << ", r=" << r << ", *p=" << *p << "n";

    *p = 7;        // change x through the pointer
    cout << "after *p=7: x=" << x << ", r=" << r << "n";

    p = &y;        // reseat pointer to y
    r = 9;         // writes to x (reference stays bound to x)
    cout << "after p=&y and r=9: x=" << x << ", y=" << y
         << ", *p=" << *p << ", r=" << r << "n";

    int* q = nullptr; // null pointer is allowed
    cout << "q is nullptr? " << (q == nullptr) << "n";
}
Output
x=42, r=42, *p=42
after *p=7: x=7, r=7
after p=&y and r=9: x=9, y=100, *p=100, r=9
q is nullptr? 1

When to use pointer vs reference (beginner guide)

Pointer or Reference? Quick decision flow
Start: You need a handle to an object
Q1: Is “no object” (absent value) valid?

No → Use Reference
Read-only: const T&
Writable: T&

Yes → Use Pointer
Type: T* and check for nullptr

Q2: Are you expressing ownership?

Yes → prefer std::unique_ptr<T> or std::shared_ptr<T>, not raw T*/T&.
No → raw pointer/reference is fine as a non-owning handle.

Tip: Need a reseatable “reference-like” thing in a container? Consider std::reference_wrapper<T>.

  • Use a reference (T& or const T&) when:
    • The parameter is required (must be non-null).
    • You want simple, object-like syntax (obj.member).
    • You’re passing large objects without copying (use const T& for read-only).
  • Use a pointer (T*) when:
    • The parameter is optional (use nullptr to mean “no object”).
    • You need to reseat which object is referenced by the handle.
    • You interact with arrays, C APIs, or do pointer arithmetic.
  • Do not use raw pointers or references to express ownership. Prefer std::unique_ptr for exclusive ownership, or std::shared_ptr for shared ownership. Raw pointers and references are non-owning views.

How do pointers and references work in function parameters and return types?

Here are common, practical patterns students will meet frequently:

  • Read-only big object: const T&
  • Optional parameter or out-parameter: T* (check for nullptr)
  • Return a reference to a subobject that outlives the function (e.g., a field of a parameter)
  • Return a pointer to indicate “found or not found” (maybe nullptr when absent)
Code
#class="cd-package">include <iostream>
class="cd-package">using class="cd-package">namespace std;

struct Shape {
    int w;
    int h;
};

void draw(const Shape& s) { // required, read-only
    cout << "draw " << s.w << "x" << s.h << "n";
}

void scale(Shape* s, int factor) { // optional: may be nullptr
    if (s) {
        s->w *= factor;
        s->h *= factor;
    }
}

Shape* findWide(Shape* arr, int n, int minW) { // may return nullptr
    for (int i = 0; i < n; ++i) {
        if (arr[i].w >= minW) return &arr[i];
    }
    return nullptr;
}

int& widthOf(Shape& s) { // safe as long as ___CDPHSTR3___ outlives the reference
    return s.w;
}

int main() {
    Shape s1{10, 20};
    Shape s2{5, 5};

    draw(s1);             // required argument
    scale(&s1, 2);        // modify through pointer
    draw(s1);

    scale(nullptr, 3);    // safe: function checks for nullptr

    Shape arr[2]{s1, s2};
    Shape* found = findWide(arr, 2, 15);
    if (found) {
        cout << "found width=" << found->w << "n";
    } else {
        cout << "none foundn";
    }

    int& wref = widthOf(s2); // reference to s2.w
    wref = 30;               // modifies s2
    cout << "s2 width now " << s2.w << "n";
}
Output
draw 10x20
draw 20x40
found width=20
s2 width now 30

Choosing parameter types: quick tips

  • Small, cheap-to-copy types (e.g., int, double, small struct): pass by value.
  • Large read-only objects (e.g., std::string, std::vector<T>): pass by const T&.
  • Optional or output parameters: use T* and document that nullptr is allowed; check inside the function.
  • Non-owning buffer parameters: in modern C++ use std::span<T> (C++20) or std::string_view for text, instead of raw T* + length.

Modern C++ notes you should know

  • nullptr (C++11) is the proper null pointer literal. Prefer it over 0 or NULL.
  • Rvalue references (T&&) enable move semantics. In templates, a parameter of the form T&& with type deduction is a forwarding reference, which preserves lvalue/rvalue-ness with std::forward.
  • Ownership: Prefer std::unique_ptr and std::shared_ptr to express owning relationships. Raw pointers and references are non-owning handles.
  • Reseatable “reference-like” handle needed? Consider std::reference_wrapper<T> (e.g., in containers) when you want reference syntax but need reseating behavior by reassigning the wrapper object.

Common mistakes with pointers and references in C++

  • Dereferencing a null pointer. Always check a T* before using *p or p-> if it may be nullptr.
  • Returning a reference to a local variable. The local dies at function end, leaving a dangling reference. Return by value or return a reference to something that still exists.
  • Assuming you can reseat a reference. r = other; assigns to the bound object; it does not change what r refers to.
  • Using raw pointers or references to express ownership. Prefer smart pointers.
  • Pointer arithmetic on non-arrays. Only use pointer arithmetic within the bounds of the same array (or one past the end for iteration, not dereference).
Code
#class="cd-package">include <iostream>
class="cd-package">using class="cd-package">namespace std;

// Wrong (do not do this):
/*
int& badFunc() {
    int local = 42;
    return local; // ERROR: returns reference to a dead local (dangling reference)
}
*/

// Correct alternatives:
// - Return by value: int goodFunc() { int local = 42; return local; }
// - Or return a reference to a valid, longer-lived object passed in.

struct Holder { int value; };

int& refToValue(Holder& h) {
    return h.value; // OK: ___CDPHSTR0___ lives outside, so returned reference is valid while ___CDPHSTR1___ lives
}

void setIfNotNull(int* p, int v) {
    if (p) *p = v; // check before dereference
}

int main() {
    Holder h{10};
    int& r = refToValue(h);
    r = 99; // modifies h.value

    int* maybe = nullptr;
    setIfNotNull(maybe, 5); // safe: function checks

    cout << "h.value=" << h.value << "n";
}
Output
h.value=99

Pointer vs reference in C++: simple rules of thumb

  1. If a function requires a parameter and will not accept “no object,” prefer T& or const T&.
  2. If “no object” is a valid case, prefer T* and document/check nullptr.
  3. Don’t use raw pointers or references to claim ownership; use smart pointers.
  4. For buffers, prefer modern non-owning views like std::span and std::string_view when available.
Beginner checklist: pointers vs references
  • Required parameter → T& or const T&. Optional → T* and check nullptr.
  • Never return a reference or pointer to a local variable.
  • Use nullptr (C++11+) instead of 0 or NULL.
  • Do not use raw pointers/references for ownership; prefer std::unique_ptr/std::shared_ptr.
  • For buffers/views prefer std::span<T> and std::string_view when available.
  • Avoid pointer arithmetic unless iterating within the same array bounds.

FAQ: Pointers vs References (for beginners)

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

A pointer is an object that holds an address; it can be nullptr and can be reseated to a different address. A reference is an alias bound at initialization to a valid object; it cannot be reseated and does not support nullptr in a defined way. That’s the core difference between pointers and references in C++.

When should I use a pointer instead of a reference?

Use a pointer when the parameter is optional (can be absent), when you need to reseat the handle to another object, when interfacing with C APIs, or when performing array-style operations. Otherwise, use references for required parameters and clear, non-null aliasing.

Can a C++ reference be null or changed to refer to another variable?

No to both in ordinary, well-formed C++. A reference must be bound to a valid object on initialization and cannot be reseated. Creating a “null reference” leads to undefined behavior and should be avoided.

How do pointers and references work in function parameters and return types?

  • Parameters: use const T& for required read-only, T& for required writable, and T* for optional writable/readable parameters.
  • Return types: return a reference only if you’re returning a subobject that will outlive the function call; otherwise, return by value. Return a pointer when you want to signal “found or not found” (nullptr).

Which is safer for beginners, pointers or references in C++?

References are usually safer as function parameters because they can’t be null and don’t require dereferencing syntax. Pointers are more flexible but require careful handling of nullptr and lifetimes. For ownership, prefer smart pointers rather than raw pointers.

Further tips and next steps

Practice by refactoring a few functions in your own code to use references for required parameters and pointers for optional ones. When you’re ready, explore move semantics (T&&) and modern non-owning views like std::span and std::string_view. For more beginner-friendly C++ tutorials, visit our C++ learning section on CodDesire.

Sources / Further reading

By now, you should have a clear picture of pointers vs references in C++, including what a pointer is in C++, what a reference is in C++, and how to choose pointer or reference in C++ functions. Keep practicing, and this core concept will soon feel natural.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted