Skip to content
C++ Better Explained
Go back
C++ Pass by Value, Reference & Pointer: Complete Guide
Edit page

C++ Pass by Value, Reference & Pointer: Complete Guide

One of the questions that trips up almost every C++ beginner is: when do I pass by value, by reference, or by pointer? The choice affects whether your function can modify the original variable, how much memory is used, and how fast your program runs.

This article breaks down all three approaches with clear examples and a practical guide on when to use each.


The Three Ways to Pass Arguments

1. Pass by Value

When you pass by value, C++ makes a copy of the argument. The function works with the copy — any changes it makes do not affect the original variable.

#include <iostream>
using namespace std;

void addTen(int x) {
    x += 10;  // Modifies the copy, not the original
    cout << "Inside function: " << x << endl;
}

int main() {
    int num = 5;
    addTen(num);
    cout << "After function: " << num << endl; // Still 5
    return 0;
}

Output:

Inside function: 15
After function: 5

The original num is unchanged because addTen received a copy.


2. Pass by Reference

When you pass by reference using &, the function receives an alias to the original variable. Any changes inside the function directly modify the original.

void addTen(int& x) {  // & means reference
    x += 10;  // Modifies the original
}

int main() {
    int num = 5;
    addTen(num);
    cout << "After function: " << num << endl; // Now 15
    return 0;
}

Output:

After function: 15

3. Pass by Pointer

When you pass by pointer, the function receives the memory address of the variable. You must dereference the pointer with * to access or modify the value.

void addTen(int* x) {  // * means pointer
    *x += 10;  // Dereference to modify the original
}

int main() {
    int num = 5;
    addTen(&num);  // Pass the address with &
    cout << "After function: " << num << endl; // Now 15
    return 0;
}

Output:

After function: 15

Side-by-Side Comparison

#include <iostream>
using namespace std;

void byValue(int x)     { x = 100; }
void byReference(int& x) { x = 100; }
void byPointer(int* x)  { *x = 100; }

int main() {
    int a = 5, b = 5, c = 5;

    byValue(a);
    byReference(b);
    byPointer(&c);

    cout << "byValue:     " << a << endl; // 5  — unchanged
    cout << "byReference: " << b << endl; // 100 — changed
    cout << "byPointer:   " << c << endl; // 100 — changed

    return 0;
}

Pass by Const Reference — The Best of Both Worlds

Passing large objects by value makes a full copy, which is expensive. Passing by reference allows the function to modify the original, which may not be what you want. The solution is const reference — efficient like a pointer, safe like pass by value:

// BAD: copies the entire string (potentially thousands of characters)
void printName(string name) {
    cout << name << endl;
}

// GOOD: no copy, but can't modify the original
void printName(const string& name) {
    cout << name << endl;
}

Const reference is the standard way to pass strings, vectors, and custom objects to functions that only need to read them.


Practical Example: Swap Two Numbers

This is a classic demonstration of why pass by reference matters:

#include <iostream>
using namespace std;

// This does NOT work — changes are lost
void swapByValue(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

// This DOES work — modifies the originals
void swapByReference(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 10, y = 20;

    swapByValue(x, y);
    cout << "After swapByValue: x=" << x << " y=" << y << endl; // 10, 20

    swapByReference(x, y);
    cout << "After swapByReference: x=" << x << " y=" << y << endl; // 20, 10

    return 0;
}

Passing Arrays

Arrays are always passed as pointers in C++. You cannot pass an array by value:

void printArray(int arr[], int size) {
    // arr is actually a pointer to the first element
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
}

For modern C++, prefer passing std::vector by const reference instead:

void printVector(const vector<int>& v) {
    for (int x : v) cout << x << " ";
}

When to Use Each

SituationUse
Small primitive type (int, char, bool) — read onlyPass by value
Need to modify the original variablePass by reference (&)
Large object (string, vector, class) — read onlyPass by const reference (const &)
Optional parameter that might be absentPass by pointer (can be nullptr)
Returning multiple values from a functionPass by reference
Working with arrays of unknown sizePass by pointer

A practical rule: for primitives use value, for objects use const reference, for output parameters use reference, for optional parameters use pointer.


References vs Pointers: Key Differences

FeatureReferencePointer
Can be null❌ No✅ Yes (nullptr)
Can be reassigned❌ No✅ Yes
Syntax to useDirect (x)Dereference (*x)
Must initialise on declaration✅ Yes❌ No
Safer to use✅ Generally❌ More error-prone

References are almost always the better choice for function parameters. Use pointers when you need nullable or reassignable semantics.



Edit page
Share this post on:

Previous Post
C++ File Handling: Reading and Writing Files with fstream
Next Post
C++ Structs Explained: How to Use struct with Examples