Menu

C++ Function Parameters: Pass by Value, Reference, Const

How arguments get into C++ functions - pass by value vs by reference, const references for cheap read-only access, default arguments, pointers, and the copy-cost gotchas that quietly slow programs down.

This page includes runnable editors - edit, run, and see output instantly.

Parameters vs Arguments

A function's parameters are the named variables in its definition; the arguments are the actual values you hand it when you call it. The previous page showed how to define and call functions - this page is about how those values actually get in, because C++ gives you several ways and the choice affects both correctness and speed.

The default in C++ is pass by value: the function receives a copy.

Inside addTen, n is a separate variable initialized from score. Reassigning n touches only that copy, so score is untouched back in main. This is safe and predictable - the function cannot accidentally clobber your data - which is exactly why it is the default.

Pass by Reference: Letting a Function Change the Caller

Sometimes you want the function to modify the caller's variable. Add an & to the parameter type and it becomes a reference - an alias for the original, not a copy:

The only difference from the first example is the &, but now n and score are the same object. This is the standard way to "return" more than one value or to update something in place. A classic use is swapping two variables:

Without the &, swapValues would shuffle two copies and main would see no change at all - a very common beginner bug.

Const References: Cheap, Read-Only Access

Pass-by-value copies the argument. For an int that costs nothing, but copying a big string or vector every call is real, wasted work. The fix is a const reference (const T&): you get reference speed (no copy) plus a compiler-enforced promise not to modify the argument.

A handy rule of thumb: pass small built-in types (int, double, char, bool, pointers) by value, and pass large objects you only need to read by const reference. Reserve a plain non-const T& for the cases where you genuinely intend to modify the caller's object.

A subtle gotcha: a plain int& n cannot bind to a temporary or a literal. addTen(5) from the first example would fail to compile if the parameter were int&, because 5 is not a variable you can alias. A const int& can bind to 5, which is one more reason const references are so widely used.

Default Arguments

You can give a parameter a fallback value so callers may omit it. If the argument is missing, the default is used:

Two rules trip people up. First, defaults must be trailing - once a parameter has a default, every parameter after it must too. You can't write void f(int a = 1, int b) because there'd be no way to supply b while skipping a. Second, when a function is declared in a header and defined elsewhere, put the default only in the declaration, never repeat it in the definition - repeating it is a compile error.

Passing Arrays and Vectors

A raw array decays to a pointer when passed, so the function loses track of its size - you almost always pass the length alongside it:

Because the array became a pointer, sizeof(arr) inside sum would give the size of a pointer, not the array - a notorious bug. In modern C++ prefer a std::vector (or std::span in C++20), passed by const reference, which carries its own size:

Note the const&: drop it and every call copies the whole vector. For a four-element vector that's harmless, but for a million elements it's a silent performance sink.

Pointer Parameters

You can also pass a pointer (T*). Like a reference, this lets the function reach the caller's data, but a pointer can be reseated or be null - so it's the right tool when "no value" is a legitimate option:

The caller passes &value to share its address, and the function writes through *out. The key difference from references: a pointer might be nullptr, so a function taking one should check before dereferencing - skipping that guard and dereferencing a null pointer is undefined behavior, usually a crash. If "no value" never makes sense, a reference is cleaner because it can't be null in the first place.

Next: References

Parameters are where references earn their keep, but references are a feature in their own right - aliases you can create for any variable, not just inside a function signature. The next page digs into how references work on their own: how to declare them, why they must be initialized immediately, the difference between an lvalue reference and a const reference, and the subtle ways a reference can end up dangling.

Frequently Asked Questions

What is the difference between pass by value and pass by reference in C++?

Pass by value copies the argument into the parameter, so changes inside the function do not affect the caller. Pass by reference (int&) makes the parameter an alias for the caller's variable, so changes are visible outside. Use void f(int x) to copy and void f(int& x) to modify the original.

When should you use a const reference parameter in C++?

Use const T& when you want to read a large object without copying it and without letting the function modify it - for example void print(const string& s). It gives you the speed of pass-by-reference with the safety of pass-by-value. For small types like int or char, plain pass-by-value is just as fast.

What are default arguments in C++?

Default arguments let a parameter take a fallback value when the caller omits it, e.g. void greet(string name = "there"). Defaults must be the trailing (rightmost) parameters, and you specify them in the declaration only, not the definition if the two are separate.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED