We’ve used functions in our code a lot so far: all of our code has been inside the “main” function, and we’ve also used a variety of functions when working with vectors and strings, such as size()
and pop_back()
. As you may see, functions are great because they allow us to to “give a name” to a certain set of code, making it easier to use that code repeatedly. For example, rather than needing to manually determine the size of a vector every time, we can just use the vector’s size()
function, which contains all the code needed for getting its size.
Here, I’ll show you how to create and use your own functions, and I’ll go into more detail about how they actually work.
Let’s start with a very simple function (that you probably won’t ever need in a real program):
#include <iostream> double add(double num1, double num2) { return num1 + num2; } int main() { std::cout << add(4.1, 1.7) << "\n"; return 0; }
In this program, “add
” is a function with two inputs (“parameters”) of data type double
(containing a decimal point): num1
and num2
. You can see that, when using the add
function in line 8, we put two numbers (separated by a comma) in parentheses. Each of these numbers is an “argument” to the function, meaning that they act as the function’s inputs. These numbers correspond to the parameters for the add
function, so 4.1 is num1
and 1.7 is num2
.
The add
function also “returns” a value of data type double
(indicated by the word double
before the function name). This means that, when the function is actually used (in line 8), the value it takes on is whatever value the function returns. In the case of the add
function, the value returned is equal to the sum of num1
and num2
. What’s returned is denoted by the use of the word return
followed by whatever you want to return.
When running this program, you should see the value “5.8” printed to the screen because the add
function adds num1
(4.1) and num2
(1.7) and returns that value.
Of course, creating a function for addition isn’t necessary since the code below will do the same thing, but this is just an example to get started.
#include <iostream> int main() { std::cout << 4.1 + 1.7 << "\n"; return 0; }
Void
Functions don’t actually need to return a value. By using a return type of “void
“, we can avoid the need for a return statement:
#include <iostream> #include <string> void print(std::string text) { std::cout << text << "\n"; } int main() { print("Hello world!"); return 0; }
Variable Scope
This is not specifically about functions, but it’s very important to start thinking about now: All variables in C++ follow specific “scoping” rules. Very generally, variables created within a set of curly braces cannot be used outside of those curly braces (excluding classes and structs, which we’ll get to later). For example, the code below will not compile:
#include <iostream> double add(double num1, double num2) { double sum = num1 + num2; return sum; } int main() { std::cout << add(4.1, 1.7) << "\n"; std::cout << sum << "\n"; return 0; }
Here, sum
can only be used within the add
function because it is created within the add function. The sum
variable is considered to be a “local” variable because it is “local” to the add
function. For completion, also be aware that function parameters (e.g. num1
and num2
above) cannot be used outside of their corresponding function.
These rules also apply to if statements and loops, so the code below won’t compile either:
#include <iostream> int main() { int number = 10; if (number > 5) { int anotherNumber = 25; } else { int anotherNumber = 20; } std::cout << anotherNumber << "\n"; return 0; }
A solution to the issue with the code above would be to declare the anotherNumber
variable outside of the if statement:
#include <iostream> int main() { int number = 10; int anotherNumber; if (number > 5) { anotherNumber = 25; } else { anotherNumber = 20; } std::cout << anotherNumber << "\n"; return 0; }
Pass by Value
Arguments (inputs) passed to a function (like the string “Hello world!” above) are passed “by value” in C++. Let’s take a look at what that means.
#include <iostream> void addFive(int number) { number += 5; } int main() { int number = 4; std::cout << "Number before adding 5: " << number << "\n"; addFive(number); std::cout << "Number after adding 5: " << number << "\n"; return 0; }
(Note: Because of variable scope rules, the number
variable in the addFive
function is completely different from the number
variable in the main
function.)
If you run the code above, you’ll see that the “Number before adding 5” is the same as the “Number after adding 5.” That is, the number
variable is not modified by the addFive
function.
Why is this? It’s all because, when passing arguments to a function, the function first makes a copy of the argument and only uses that copy within the function. Here’s another example:
#include <iostream> #include <string> void removeLastLetter(std::string str) { str.pop_back(); } int main() { std::string hello = "Hello world!"; std::cout << "String before removing last letter: " << hello << "\n"; removeLastLetter(hello); std::cout << "String after removing last letter: " << hello << "\n"; return 0; }
Just like the last example, the string’s values before and after using the removeLastLetter
function are equivalent because the hello
variable itself is not actually modified by the removeLastLetter
function. Only a copy of the variable is modified.
Later, when going over “pointers” in C++, you’ll see one way of working around this issue. Another is to just return the modified value from the function:
#include <iostream> #include <string> std::string removeLastLetter(std::string str) { str.pop_back(); return str; } int main() { std::string hello = "Hello world!"; std::cout << "String before removing last letter: " << hello << "\n"; hello = removeLastLetter(hello); std::cout << "String after removing last letter: " << hello << "\n"; return 0; }
Recursion
Functions in C++ are actually allowed to call themselves. This is what’s known as recursion.
One way of using recursion is for performing the factorial operation, which takes a number and multiplies it by each number smaller than it, down to 1. For example, 5! (5 factorial) = 5 * 4 * 3 * 2 * 1 = 120. Here’s how recursion can be used for this:
#include <iostream> int factorial(int number) { if (number == 1) { return 1; } return number * factorial(number - 1); } int main() { std::cout << "4! = " << factorial(4) << "\n"; std::cout << "5! = " << factorial(5) << "\n"; return 0; }
A good way to visualize this approach is with a tree:

factorial(4)
is equal to 4 * factorial(3)
, which is equal to 4 * 3 * factorial(2)
, and so on…
One thing to be very careful of when using recursion is to ensure that there’s some way for the recursion to end. In the case of factorial, the recursive process ends when reaching a value of 1. You can see this in the code where just the number 1 is returned, as opposed to further evaluation of the factorial. Without doing this, the recursion would go on forever until your computer can recurse no longer.
It turns out that recursion is never the only way of solving a problem, so there’s always some solution to a problem just using loops. For many problems, however, recursion is the simplest way to go.
Challenge Problem
Write a program containing a function called “factorial
” that returns the factorial of a parameter without using recursion.
If you get stuck, check out my solution here.