Passing arguments: value, reference, and pointer
Choose how a C++ function receives an argument: as an independent value, an alias to an existing object, or an address that may be absent.
Prerequisites and outcomes
Prerequisites: variables and functions. This lesson introduces the small amount of pointer and reference syntax needed for parameter choices. A reference is an alias for an existing object; a pointer stores an address and may be null. Their full treatment comes later in Lessons 37 and 38. After this lesson, you should be able to predict whether a function can change its caller's object, decide whether an argument may be absent, and explain the cost and safety trade-offs.
Three different contracts
| Parameter form | Meaning | Can change caller's object? | Can be null? |
|---|---|---|---|
T value | Function receives its own parameter object | No, not through this parameter | No |
T& value | Alias to an existing object | Yes | No |
const T& value | Read-only alias | Not through this reference | No |
T* value | Address of an object, or null | Yes, if valid and non-const | Yes |
A value parameter is a separate parameter object. If the copied value is itself a pointer, the pointer value is copied, but the function may still modify the pointed-to object through a valid non-const pointer.
Pass by value: a separate parameter
#include <iostream>
void add_one(int number) {
++number;
std::cout << "Inside function: " << number << '\n';
}
int main() {
int score = 10;
add_one(score);
std::cout << "In main: " << score << '\n';
}For an int, the value is copied into the parameter. Incrementing number changes that parameter, not score. For a large object, copying may be expensive; for small scalar types it is usually the clearest choice.
Pass by reference: an alias
#include <iostream>
void add_one(int& number) {
++number;
}
int main() {
int score = 10;
add_one(score);
std::cout << score << '\n';
}The parameter is another name for the caller's object. A non-const reference communicates that the function may modify it. Use this when modification is part of the function's contract.
Pass by const reference: read without copying
#include <iostream>
#include <string>
void print_name(const std::string& name) {
std::cout << name << '\n';
}
int main() {
const std::string name{"Ada"};
print_name(name);
}This avoids copying a potentially large string and prevents the function from modifying it through this parameter. The caller must still keep the object alive for the call.
Pass by pointer: optional object
#include <iostream>
void reset_if_present(int* value) {
if (value != nullptr) {
*value = 0;
}
}
int main() {
int points = 9;
reset_if_present(&points);
reset_if_present(nullptr);
std::cout << points << '\n';
}A pointer is useful when absence is meaningful or when working with APIs that use pointer semantics. Always check for null before dereferencing unless the function contract explicitly requires a valid pointer.
Worked trace: one copy and one alias
#include <iostream>
void update(int value, int& reference) {
value += 3; // changes only the local copy
reference += 5; // changes the caller's object
std::cout << "Local copy: " << value << '\n';
}
int main() {
int score{4};
update(score, score);
std::cout << "Caller object: " << score << '\n';
}Both parameters are initialized from score at the call. value receives a separate copy of 4, while reference becomes an alias for score. Adding 3 changes only the local copy to 7; adding 5 through the reference changes the caller's score from 4 to 9. The two output lines make the independent parameter object and the alias observable.
Common mistakes
- Returning a reference or pointer to a local variable: the local object dies when the function returns.
- Using a non-const reference when the function should only read: this makes the contract unnecessarily broad.
- Dereferencing a nullable pointer without checking it.
- Assuming
const T&makes the original object globally immutable; it only restricts access through that reference.
Further reading: for a deeper treatment, continue later with Pointers (Lesson 37) and References (Lesson 38).
Practice
- Which parameter should a read-only function use for a large string?
- Which form communicates that no object may be absent?
- What happens if a function increments an
intpassed by value?
Show reasoned answers
const std::string&avoids a copy and prevents modification through the parameter.- A reference, if the function requires an existing object.
- Only the local parameter changes; the caller's integer remains unchanged.
Summary and next step
Choose parameter types as part of the function's contract: value for an independent copy, const reference for read-only access to an existing object, non-const reference for intentional modification, and pointer when optionality or pointer semantics matter. Next, study scope, lifetime, and storage duration.