Pass by value vs pointer vs reference in C++

Short answer: pass by value gives the function a copy (changes don't escape). Pointers and references both give it the real object (changes escape). A pointer can be null and can move; a reference is an alias that's bound forever. Default to value for small things, const reference for big read-only things.

Watch the difference happen — both functions try to set their argument to zero, and only one succeeds:

#include <iostream>
using namespace std;

void tryToZero(int x)    { x = 0; }    // a copy — caller never sees it
void actuallyZero(int* x) { *x = 0; }  // the real object's address

int main() {
    int a = 5, b = 5;
    tryToZero(a);
    cout << "after tryToZero:   " << a << endl;
    actuallyZero(&b);
    cout << "after actuallyZero: " << b << endl;
    return 0;
}

The demos on this page use pointers because our in-browser engine doesn't support reference parameters (int&) yet — but every rule below covers references too, and we'll show their syntax in code blocks.

The three options, side by side

by valueby pointerby reference
Syntaxf(int x)f(int* x), call f(&a)f(int& x), call f(a)
What arrivesA copyThe object's addressAn alias for the object
Caller sees changes?NoYesYes
Can be "nothing"?NoYes — null pointerNo — must be bound
Copy costFull copy every callJust an addressJust an alias

The classic use case: swapping two variables

A swap must modify the caller's variables, so copies are useless — you need addresses:

#include <iostream>
using namespace std;

void mySwap(int* a, int* b) {
    int t = *a;   // save the value a points to
    *a = *b;
    *b = t;
}

int main() {
    int x = 1, y = 2;
    mySwap(&x, &y);
    cout << x << " " << y << endl;   // 2 1
    return 0;
}

The reference version reads more cleanly at the call site — same mechanics, no &/* noise:

void mySwap(int& a, int& b) {
    int t = a;
    a = b;
    b = t;
}
// call: mySwap(x, y);   // looks like by-value, acts like a pointer

Surprise: arrays are always passed like pointers

When you pass a C-style array, what actually arrives is a pointer to its first element — the size never travels with it. That's why every function taking an array takes a length too:

#include <iostream>
using namespace std;

int sum(int arr[], int n) {   // arr is really int*
    int s = 0;
    for (int i = 0; i < n; i++) s += arr[i];
    return s;
}

int main() {
    int a[3] = {10, 20, 30};
    cout << sum(a, 3) << endl;
    return 0;
}

So which one do I use?

Interview follow-ups worth having ready

  1. "Pointer vs reference — give me one concrete reason for each." Pointer: it can be null / can be reseated. Reference: it must always refer to something and never needs * checks.
  2. "Why pass big objects by const reference?" — avoiding the copy dominates; const guarantees read-only.
  3. "What's wrong with int& f() returning a reference to a local?" — the local dies when the function returns; the reference outlives its object. Undefined behavior, and a favorite trick question.
  4. "Does pass by value always cost a copy?" — since C++11, move semantics can turn expensive copies of temporaries into cheap moves.

Practice in the playground

  1. Write addOne(int* p) that increments the caller's variable; call it on x and print.
  2. Change mySwap to take int* by value — i.e. swap only the pointers inside the function — and explain to yourself why the caller sees no change.
  3. Write maxOf(int a, int b, int c) by value and confirm small-value passing is the right default.

Related: 15 beginner interview questions (pointers vs references is Q1 there), ++i vs i++, and integer division.