C++ Lesson 26 – References | Dataplexa
Advanced C++ Β· Lesson 26

References

A pointer stores an address and you must follow it with a star. A reference is simpler: it is a second name for a variable that already exists. No address, no star, no checking for null β€” the reference is the variable, under another name. If pointers are the most powerful tool in C++, references are the most used one: almost every function you write from now on will take its arguments by reference.

1. What Is a Reference?

You declare a reference by putting an & after the type: int& r = age; makes r a second name for age. There is no new box in memory β€” r and age are the same variable, seen through two names. A reference must be given a variable at once; there is no such thing as an empty reference. And once it is bound, it cannot be re-bound: writing r = other; changes the value of age, it does not make r point at other.

Code story: Here is your first reference. The reference r is not a copy of age β€” it is a second name for the very same variable. Watch changing r change age itself.

#include <iostream>
using namespace std;

int main() {
    int age = 25;
    int& r = age;
    cout << "Age: " << age << endl;
    cout << "Reference: " << r << endl;
    r = 30;
    cout << "Age now: " << age << endl;
    return 0;
}
Age: 25
Reference: 25
Age now: 30

What just happened? The line r = 30; writes into age, because r is age under another name β€” there are two names and one variable. Nothing was copied, nothing was pointed at, and no star was needed. Compare this with Lesson 25, where the same change needed *p = 30; through a pointer. The reference hides the machinery and leaves only the meaning.

2. References vs Pointers

References and pointers are cousins: under the hood, a reference is usually implemented as a pointer that the compiler manages for you. But the rules you live by are different. A reference must always refer to something, it can never be empty, and it can never be re-bound to a different variable. A pointer can be null, can be re-pointed, and must be followed with a star. When a function needs an alias it will always use, take a reference. When it needs something that may be absent, or may need to point elsewhere later, take a pointer.

Aspect Reference Pointer
What it isA second name for a variableA variable that holds an address
Can it be empty?No β€” it must be bound at onceYes β€” it can hold nullptr
Can it point elsewhere later?No β€” once bound, always boundYes β€” assign a new address any time
How you use the valueUse the name directly, no starFollow it with * to reach the value

3. Pass by Reference β€” Letting a Function Change the Caller

By default, C++ passes arguments by value: the function gets a copy, and changing the copy changes nothing outside. Pass by reference changes the deal: the function receives the caller's variable itself, under a new name, so anything it does to the parameter happens to the caller's variable. The classic example is a swap β€” two values exchange places β€” which is impossible with pass by value, because the function would only be swapping its own copies.

Code story: Here is the reason references exist. A swap function takes two numbers by reference, so it can reach into the caller's variables and exchange them. Watch x and y trade places β€” with pass by value, this would be impossible.

#include <iostream>
using namespace std;

void swapValues(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5;
    int y = 9;
    swapValues(x, y);
    cout << "x: " << x << endl;
    cout << "y: " << y << endl;
    return 0;
}
x: 9
y: 5

What just happened? Inside swapValues, a is x and b is y β€” not copies of them, the very same variables. The three lines of the swap therefore rewrite the caller's x and y. If the parameters had been plain ints, the function would have swapped its own copies and main() would still print 5 and 9. Whenever a function must change its caller's data, pass by reference is the tool.

4. const References β€” Read Without Copying

Sometimes a function only needs to read a value, not change it β€” and the value is big, like a long string, where copying it would waste time and memory. The answer is a const reference: const string& lets the function read the caller's string directly, with no copy, and the const promises the function cannot change it. You already met this pattern in Lesson 24, where the Library took members by reference, and you will see it everywhere in the STL lessons.

Code story: Here is the everyday use of references. A function that only reads a long string takes it by constant reference β€” no copy is made, and the function cannot change it by accident. Watch it greet both a variable and a literal.

#include <iostream>
#include <string>
using namespace std;

void printName(const string& name) {
    cout << "Hello, " << name << "!" << endl;
}

int main() {
    string user = "Ada";
    printName(user);
    printName("Grace");
    return 0;
}
Hello, Ada!
Hello, Grace!

What just happened? The function printName() takes its argument by const reference, so it reads the caller's string without copying a single character. It even accepts a literal, Grace, which becomes a temporary string the reference binds to. And the const is a real guard: try writing name = "Bob"; inside printName() and the compiler refuses. The rule is simple: if a function reads a value and does not need to own it, take it by const reference.

5. Returning a Reference β€” Careful

A function can also hand back a reference β€” and the caller can write through it, as if the function had handed back the variable itself. This is useful: a function that finds the larger of two numbers can return a reference to it, and the caller can then change that number. But there is one hard rule: never return a reference to a local variable. A local dies when the function returns, so the reference would point at a dead variable β€” a dangling reference β€” and using it is undefined behaviour. Return references only to things that outlive the call: a parameter, a member, or a static variable.

Code story: Here a function hands back a reference to the larger number β€” and the caller writes straight through it. Watch y change to 100, because larger(x, y) IS y. Never return a reference to a local variable: it dies when the function ends.

#include <iostream>
using namespace std;

int& larger(int& a, int& b) {
    if (a > b) return a;
    return b;
}

int main() {
    int x = 5;
    int y = 9;
    larger(x, y) = 100;
    cout << "x: " << x << endl;
    cout << "y: " << y << endl;
    return 0;
}
x: 5
y: 100

What just happened? The call larger(x, y) returns a reference to y, because 9 is bigger than 5. The assignment = 100 therefore writes into y through that reference. x never changes. This is safe only because the reference points at a parameter β€” something that belongs to the caller and outlives the call. If larger() had returned a reference to a local variable declared inside it, that variable would be gone the moment the function returned, and the assignment would write into dead memory.

6. Common Mistakes with References

Mistake What happens Fix
Declaring a reference without binding itThe program does not compile β€” a reference must refer to somethingBind it at once: int& r = x;
Thinking r = y re-binds the referenceIt copies y into x instead β€” the reference stays bound to xRemember: assignment changes the value, never the binding
Returning a reference to a local variableThe reference dangles β€” the local is gone when the function returnsReturn references only to parameters, members or statics
Passing by value when you meant referenceThe function changes only its own copy, and the caller sees nothingWrite & in the parameter when the function must change the caller
Using a reference where null is neededThere is no empty reference to mean not setUse a pointer when absence is a real possibility

Practice β€” References

Try these on your own. Compile and run each one.

  1. Declare an int and a reference to it. Change the value through the reference and print the original.
  2. Write a swap function that exchanges two doubles using references.
  3. Write a function that doubles a number in place (void doubleIt(int& n)) and call it.
  4. Write a function that takes a const string& and prints its length without copying it.

What is a reference?


int x = 5; int& r = x; r = 9; What is x now?


Why take a large object by const reference instead of by value?


Fill in the blanks β€” type the missing word into the box.

A reference is a second name, or , for a variable.


To declare a reference, write the type and then .


To read a large object without copying it and without changing it, take it by reference.


Up Next

Memory Management

Next, stack versus heap β€” where your data lives.