Functions

Functions

Functions help break down complex tasks into smaller, manageable parts. This improves organization, making the code clearer and more efficient.

Function Syntax:

void function_name() 
{
	std::cout << "This is an example to show that the function can execute code. \nYou can do a lot more with functions than simplify text outputs!";
}

To execute the code inside your functions you call it by using its name followed by a set of parenthesis function_name();.

#include <iostream>

void function_name() 
{
	std::cout << "Code execution reached function!\n";
}

int main() 
{
	std::cout << "Code started \n";
	function_name();
	std::cout << "Code execution has returned to main\n";
}
Code started
Code execution reached function!
Code execution has returned to main

Code execution moves into the function when it reaches the function call. At the end of the function, the code execution returns to where the function was called from.

You cannot declare a function inside another function. Since int main() is a function, we need to declare it in the global scope.

Return types and the return keyword

This piece of code gathers data from the user.

// Get an integer from the user input
std::cout << "input an integer\n";
int input;
input << std::cin;

If we move it into a function we wouldn't be able to access the data because the input variable is inside the function scope.

#include <iostream>

void get_user_input_int()
{
	// Get an integer from the user input
	std::cout << "input an integer\n";
	int input;
	input << std::cin;
}

int main() 
{
	get_user_input_int();
	std::cout << input; // does not work ( variable outside scope )
}

This code will not compile. input is not found inside int main

We could move the variable input into the global scope, but this is messy and pollutes the global scope.

We can return a value from a function so that we can use it after calling the function. We do this by specifying what datatype we want to return where it previously said void.

By specifying a return type, we are promising to return a value, so the program will not work if we don't. To return a value, we use the return keyword. This will send the code execution back to where the function was called with the value we return.

We can assign the returned value from the function call to a variable inside int main().

#include <iostream>

// The int keyword shows that this function will return an integer value
int get_userinput_int() 
{
	std::cout<<”input an integer\n”;
	int int_input;
	int_input<<std::cin;
	// Returning the input so that we can get the value of input from where the function is called
	return int_input;
}

int main() {
	// Assigning the return value to a variable in the main scope
	int input = get_userinput_int();
	std::cout << input;
}

Code execution jumps back to the function call when it returns a value. If you put any code after the return call, it will not execute.

#include <iostream>

int get_userinput_int() 
{
	std::cout<<”Output1 - ”;
	return 15;
	std::cout<<”Output2”;
}

int main() 
{
	int var = get_userinput_int();
	std::cout << var;
}
1 - 15

void means that the function doesn't return a value. You can use the return keyword by itself in a void function to return code execution, but you cannot use it to return a value.

void example()
{
	std::cout <<"This will print";
	return;
	std::cout << "This will not print";
}

Parameters

We can pass data from the function call to the function using parameters. We specify the function parameters in the function declaration.

#include <iostream>

void example_function(int parameter_example)
{
        std::cout << parameter_example;
}

int main() 
{
        example_function(7);
}

In this example we pass 7 into the function example_function, where it outputs it to the console.

We can include multiple variables by separating them with commas. Commas are also used to separate the arguments we pass into a function.

#include <iostream>

void example_function(int input1, float input2) 
{
      std::cout << "input1:" << input1 "\ninput2:"<< input2;
}

int main() {
      example_function (4, 5.5f);
}
input1: 4
input2: 5.5

The parameter variable is a copy of the value passed into it. Changing the value of the parameter variable inside the function will not change the value of the variable passed as an argument.

Here’s an example that shows you the value of variables throughout the program.

#include <iostream>

void func(int var)
{
	std::cout << "2 - " << var  <<'\n'; // var = 4
	var = 2;
	std::cout << "3 - " << var  <<'\n'; // var = 2
}

int main() 
{
	int var = 4;
	std::cout << "1 - " << var  <<'\n'; // var = 4 
	func(var);
	std::cout << "4 - " << var  <<'\n'; // var = 4 
}
1 - 4
2 - 4
3 - 2
4 - 4

Function declaration and definitions

You can't call a function above where it's declared. This is the result of code execution moving from top to bottom.

You can declare a function without defining it by providing the function signature.

datatype function_name(parameters);

The function signature is a combination of the function's name, return type, and the types of its parameters. If you end the declaration with a semicolon instead of providing the block of code, you can define it later in the code.

This gives the compiler enough information to understand how to call the function. The compiler will link the declaration and definition later.

The separation of declaration and definition allows you to organize your code better.

Recursive calling

Recursive calling is when a function calls itself. This causes code execution to jump into a new instance of the function.

#include <iostream>

int factorial(int n) {
    // Base case
    if (n == 1)
        return 1;

    // Recursive case
    return n * factorial(n - 1);
}

int main() {
	int num = 7;
	std::cout << "Factorial of " << num << " is: " << factorial(num);
	return 0;
}

This program creates an infinite loop if you call it with a negative number. This is because n will never be 1.

This factorial function gets the factorial value by recursively calling itself. It repeatedly returns the number we want to get the factorial off multiplied by the factorial of the number below. factorial(5) will return 5 * factorial(4). factorial(4) will then return 4*factorial(3). This continues until factorial(1), which returns 1, making the final expression 5*4*3*2*1.


Mutual recursion occurs when two or more functions call each other in a circular manner. This is, if function1 depends on function2, and function2 depends on function1 to complete their tasks.

You can forward declare your functions to let 2 or more functions call each other.

For example, these 2 functions decrement a number and pass it to the other until the number is 0.

#include <iostream>

// Function declarations to allow for calling each other
int function1(int n);
int function2(int n);

int main()
{
	function1(10);
}

// Definitions of the functions
int function1(int n) 
{
	std::cout << "fun1-" << n << "\n";

	if (n<=0)
		return 0;

	return function2(n-1);
}

// Definitions of the functions
int function2(int n) 
{
	std::cout << "fun2-" << n << "\n";

	if (n<=0)
		return 0;

	return function1(n-1);
}
fun1-10
fun2-9
fun1-8
fun2-7
fun1-6
fun2-5
fun1-4
fun2-3
fun1-2
fun2-1
fun1-0
  • The program starts with the main() function which calls function1 with an argument of 10.
  • Inside function1, the code prints "fun1-10".
  • Since n is not less than or equal to 0, it calls function2 with an argument of 9 (n-1).
  • The steps then repeat themselves until one of the functions is called with the argument of 0.
  • When n is less than or equal to 0, the function (whichever it is at the time) will return 0 and the recursion will stop.

I would recommend using normal loops over recursive calling if you have the option. They are generally easier to understand, so the chance of making an infinite loop or running into other unintended behaviors is lower.

Function overloading

Functions cannot have the same name AND parameter sets as another function. The compiler wouldn't know which function you are referring to when trying to call one of them.

You can have multiple functions with the same name if they have different parameters. Having multiple functions with the same name is called function overloading.

When the function names match, the compiler will look at the arguments to determine a fit based on the parameters.

Here's a function that takes 2 integers and one that takes one integer.

#include <iostream>

void list_ints(int i1, int i2, int i3) 
{
	std::cout << "Your 3 integers are: " << i1 << ", "<< i2 << ", "<< i3 << "\n";
}
void list_ints(int i1, int i2) 
{
	std::cout << "Your 2 integers are: " << i1 << ", "<< i2 << "\n";
}

int main() 
{
	list_ints(2, 4, 6);
	list_ints(3,5);
}
Your 3 integers are: 2, 4, 6
Your 2 integers are 3, 5

When we call them and run our program we can see that the compiler has no issue determining which one to use.


This isn't limited to the number of parameters. The compiler also looks at the data type of the arguments to match with the parameters. Here are 2 functions with the same name, but with different datatypes.

#include <iostream>

void function(int i) 
{
	std::cout << "Your integer is: " << i << "\n";
}

void function(float i) 
{
	std::cout << "Your float is: " << i;
}

int main() 
{
	function(2);
	function(2.f);
}
Your integer is: 2
Your float is: 2

Default parameter values

Default parameter values let you specify a value that a parameter should have if no argument is provided. This lets you call the function with fewer arguments.

You can set a default parameter value by assigning a value to a parameter variable.

#include <iostream>

void function_example(int v1, int v2, int v3=0, int v4=0)
{
	std::cout << v1 << "-" << v2 << "-" << v3 << "-" << v4 << "\n";
}

int main() 
{
	function_example(5, 5, 5, 5);
	function_example(5, 5, 5);
	function_example(5, 5);
}
5 - 5 - 5 - 5
5 - 5 - 5 - 0
5 - 5 - 0 - 0

You can call this function with 2, 3, or 4 float arguments. v3 and v4 default to 0 if you don't specify them.

The compiler still recognizes the function. The parameter variables get the value assigned to them in the declaration instead.

The default values can only be set from the back. There is no way for you to pass the fourth parameter without passing the third, second, and first parameter.

// This will not be valid because v4 and v3 do not have default cases, so v2 cannot have a default case.
void function(int v1, int v2 = 0, int v3, int v4);

This is a syntax error, and will not compile.

If you have 5 parameters, you need your 4th and 5th parameters to have a default cause if you want your 3rd parameter to have a default case.

The entry point of a console program

The entry point (int main()) is a function that gets called by the operating system when we start our program. The return value indicates if our program ran successfully. A return value of 0 means that the program executed successfully. A non-0 value means that the program encountered a problem and terminated prematurely. In that case, the return value indicates what the error was.

Despite it having a return type, it doesn't require you to return a value.

Questions

Questions

Questions

Questions

Questions

Questions

Questions

Questions