C++ Course
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 is | A second name for a variable | A variable that holds an address |
| Can it be empty? | No β it must be bound at once | Yes β it can hold nullptr |
| Can it point elsewhere later? | No β once bound, always bound | Yes β assign a new address any time |
| How you use the value | Use the name directly, no star | Follow 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 it | The program does not compile β a reference must refer to something | Bind it at once: int& r = x; |
| Thinking r = y re-binds the reference | It copies y into x instead β the reference stays bound to x | Remember: assignment changes the value, never the binding |
| Returning a reference to a local variable | The reference dangles β the local is gone when the function returns | Return references only to parameters, members or statics |
| Passing by value when you meant reference | The function changes only its own copy, and the caller sees nothing | Write & in the parameter when the function must change the caller |
| Using a reference where null is needed | There is no empty reference to mean not set | Use a pointer when absence is a real possibility |
Practice β References
Try these on your own. Compile and run each one.
- Declare an int and a reference to it. Change the value through the reference and print the original.
- Write a swap function that exchanges two doubles using references.
- Write a function that doubles a number in place (void doubleIt(int& n)) and call it.
- 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.
