Note, this article is not finished! You can help by editing this doc page.
Functions
In this lesson you will learn how to teach a program to perform actions, with the ability to reuse them.
Motivation
One of the advantages of using functions is the reduction of duplicates in the code. See example below, the version without and with functions. At this point you do not need to understand the notation of the example with the function - we will explain it later in this lesson.
- ⚠ Without functions
- 🟢 With function
#include <iostream>
#include <vector>
int main()
{
std::vector<int> numbers = {1, 4, 13, 15};
for (int i : numbers) {
std::cout << i << ' ';
}
std::cout << '\n';
numbers.push_back(13);
for (int i : numbers) {
std::cout << i << ' ';
}
std::cout << '\n';
numbers.push_front(3);
for (int i : numbers) {
std::cout << i << ' ';
}
std::cout << '\n';
}
#include <iostream>
#include <vector>
void print(std::vector<int> vec)
{
for (int i : vec) {
std::cout << i << ' ';
}
std::cout << '\n';
}
int main()
{
std::vector<int> numbers = {1, 4, 13, 15};
print(numbers);
numbers.push_back(13);
print(numbers);
numbers.push_front(3);
print(numbers);
}
In the example above, we create an array of numbers and modify it, displaying the contents each time. The difference between the two versions is that the first one contains a piece of code that we have copy-pasted several times. In the second, more correct version, we teach our program once how to display an array of numbers and then use it repeatedly when we need it.
Functions have many other uses and we will show some of them in this lesson. But for now we need to go back to the basics.
Introduction
A function is a separate piece of code that we can reuse many times.
From the very beginning of our study, we included a special function in our programs,
which is main
.
int main() {
// program code
}
A computer running a program finds the function main
and then executes it,
passing each statement inside, line by line.
Creating functions
We will start with the simplest features, gradually moving to more and more advanced features.
Without parameters
Scheme of the simplest functionAs we can see above, when defining our own function, we have to take care of its name and body, sticking to the
syntax. After the word void
we write function name, then we put empty parentheses
(we'll talk about that later in this lesson), then without a semicolon we place below
block of code, which we call the function body.
When naming a function, follow the same rules as for variable names.
Now we already have our function defined. This means that whenever we want to, we can call it. We do it this way:
print();
Note that we put a semicolon after the function call, because it's also the end of the statement.
Let's write an example function that displays 10 even numbers and use it a few times:
#include <iostream>
// Function definition
void print_10_even_numbers()
{
for (int i = 0; i < 10; i++)
std::cout << (i * 2) << ' ';
std::cout << '\n';
}
int main()
{
// Calling the function
print_10_even_numbers();
print_10_even_numbers();
print_10_even_numbers();
}
0 2 4 6 8 10 12 14 16 18
0 2 4 6 8 10 12 14 16 18
0 2 4 6 8 10 12 14 16 18
With parameters
Scheme of a function with parametersThe behavior of the function can depend on parameters. If you want the function to display any number of even numbers, we can create a parameter in the function, that will control it. A parameter is a variable inside the function, which we create inside the parentheses
#include <iostream>
void print_even_numbers(int how_many)
{
for (int i = 0; i < how_many; i++)
std::cout << (i * 2) << ' ';
std::cout << '\n';
}
int main()
{
print_even_numbers(10);
print_even_numbers(5);
print_even_numbers(3);
}
The above notation
print_even_numbers(10);
means that when called, the how_many
parameter inside this function
will be assigned the value 10
. We can pass any number of parameters to the function.
Separate them by commas:
#include <iostream>
void print_bigger_number(int a, int b)
{
if (a > b)
std::cout << a << '\n';
else
std::cout << b << '\n';
}
int main() {
print_bigger_number(3, 5);
print_bigger_number(5, 3);
print_bigger_number(3, 10);
}
Notice that we place the function parameters after a comma, each time specifying its type. A common mistake among beginners is omitting the type in the following parameters.
- ❌ Bad
- ✔ Good
void print_bigger_number(int a, b)
void print_bigger_number(int a, int b)
Return statement
We can tell the program to return from a function earlier, using a statement:
return;
Upon encountering it, the program stops executing further instructions in the function
and returns to the place from which it was called.
Let's recall the function we created earlier - print_even_numbers
void print_even_numbers(int how_many)
{
for (int i = 0; i < how_many; i++)
std::cout << (i * 2) << ' ';
std::cout << '\n';
}
What happens if we pass a negative number to how_many
?
print_even_numbers(-10);
Executing a function with such a parameter value makes no sense, so we can detect this at
the very beginning and use the return
:
void print_even_numbers(int how_many)
{
if (how_many <= 0)
return;
for (int i = 0; i < how_many; i++)
std::cout << (i * 2) << ' ';
std::cout << '\n';
}
Now if we pass a value less than or equal to zero to a function parameter, the function will be terminated at the beginning.
Return value
Schema of a function with parameters and return valueFunctions can produce a result after their execution. It will be easier to understand this if we use an analogy to real life. Father sends his child to the store to buy 10 eggs. When the child returns, he will want to know, whether the child managed to buy as many as he asked for or not. In the same way, we, performing some functions in code, we want to know the result, the return value.
Return value type
It is required to specify what type the return value is. We specify it before the function name, example:
int sum(int from, int to)
void print(int number)
Notice that we have introduced a new type: void
. If we write it in place of the return type,
it means that the function does not return a value, i.e. we do not need to know its result.
Note that we used void
in the previous sections of this lesson for this very reason.
The void
type cannot be used to create a variable:
void variable;
A variable inherently stores a value, which would be impossible if it were of type void
.
Usage
Let's implement the function from the diagram above. It is to count the sum of all numbers in the range from
to to
inclusive.
int sum(int from, int to)
{
int result = 0;
for (int i = from; i <= to; i++)
result += i;
return result;
}
We can use the returned value, for example, by writing it to a variable:
int s = sum(10, 100);
... or use in an expression (for example, as a function parameter):
std::cout << "Sum of the numbers in range [10; 100] equals: " << sum(10, 100);
Requirements
A function that returns some value (anything other than a `void' type), must at the very end of its execution return some value.
int sum(int from, int to)
{
int result = 0;
for (int i = from; i <= to; i++)
result += i;
// ❌ No return statement!
}
An exception is the main
function, which, despite returning an int
type,
performs an automatic return of a value 0
when return
is omitted:
int main() {
// No "return" statement
}
We will say more about the main
function in the future.
Declaration and definition
In order to use the above sum
function, we need to make sure that it is before this use, for example:
- ❌ Bad
- ✔ Good
#include <iostream>
int main()
{
// Error: used before the definition
std::cout << "Sum of the numbers in range [10; 100] equals: " << sum(10, 100);
}
int sum(int from, int to)
{
int result = 0;
for (int i = from; i <= to; i++)
result += i;
return result;
}
#include <iostream>
int sum(int from, int to)
{
int result = 0;
for (int i = from; i <= to; i++)
result += i;
return result;
}
int main()
{
std::cout << "Sum of the numbers in range [10; 100] equals: " << sum(10, 100);
}
Making sure that the order always matches is troublesome and sometimes even impossible. To fix the error in the above example, we have to use so called function declaration.
So far, when creating functions we used function definition which besides declaration, also contains its entire implementation (body). Declaring a function looks like defining it, without providing its body. We also need to take care to put a semicolon after the declaration.
int sum(int from, int to)
{
int result = 0;
for (int i = from; i <= to; i++)
result += i;
return result;
}
int sum(int from, int to);
The rule is that in order to use a function, it must be declared beforehand. The definition must appear, but it does not matter if it is before or after the use. Here are two examples:
- ✔ OK
- ✔ OK (definition before)
- ✔ OK (multiple decl.)
#include <iostream>
// 🟣 Declaration
int sum(int from, int to);
int main()
{
std::cout << "Sum of the numbers in range [10; 100] equals: " << sum(10, 100);
}
// 🔵 Definition
int sum(int from, int to)
{
int result = 0;
for (int i = from; i <= to; i++)
result += i;
return result;
}
#include <iostream>
// 🟣 Declaration
int sum(int from, int to);
// 🔵 Definition
int sum(int from, int to)
{
int result = 0;
for (int i = from; i <= to; i++)
result += i;
return result;
}
int main()
{
std::cout << "Sum of the numbers in range [10; 100] equals: " << sum(10, 100);
}
#include <iostream>
// 🟣 Declarations
int sum(int from, int to);
int sum(int from, int to); // OK, multiple declarations are acceptable
int sum(int from, int to); // but unnecessary 🤔
int main()
{
std::cout << "Sum of the numbers in range [10; 100] equals: " << sum(10, 100);
}
// 🔵 Definition
int sum(int from, int to)
{
int result = 0;
for (int i = from; i <= to; i++)
result += i;
return result;
}
The definition must appear in the code only once (there can be multiple declarations).
Summary
- We use functions to group code into named blocks.
- Functions can be called (invoked) to execute the code inside them.
- Functions can return a value.
- Functions can accept parameters.
- Functions can be declared and defined separately.