When writing a sequential program without functions oftentimes the same code needs to be written in multiple places. Take for example user input, where the input must be validated:
int v = 0; do { if (cin.fail() || v < 0 || v > 100) { cout << "Error\n"; cin.clear(); cin.ignore(256, ‘\n’); } cout << "Value: "; cin >> v; } while (cin.fail() || v < 0 || v > 100);
Every time a user input is needed this large loop needs to be used to make sure that the user enters a valid number that is in range. So if 10 inputs are needed then this code would be needed 10 times over, with only small parts of the loop changing each time it is used: the range, the prompt, the error message, and the variable being saved to. Rather than repeating this loop 10 times in the program, a user-defined function can be written to abstract the concept of getting an integer from the user. This way the code written for the function can be reused each time an integer is needed from the user.
A function is a named block of code that performs a specific task. A function can take in input values (parameters) and process them to perform a specific task. A user-defined function is a function the programmer creates to perform a specific task. Since C++ does not assume every function the user may need, the programmer needs to be able to write their own functions. For this the programmer can use user-defined functions to avoid repeating code.
There are two types of functions:
Void Functions - Functions that does not return a value when they finish executing.
Value-Returning Functions - Functions that return a single value of a specific type when they finish executing.
To define a function, a programmer must specify the following elements:
Function Identifier - The name given to a function.
Parameters - Variables declared in a function header to act as inputs to receive values the function will process.
Return Value - The actual data (if any) sent back when a function completes.
Return Type - The kind of data (if any) a return value is.
Function Header - The first line of a function definition that specifies a function’s return type, identifier, and parameters.
Function Body - The set of instructions (a block of code) that execute when a function is called.
Function Prototype - A declaration that tells the compiler a function exists and how it should be used before the function is fully defined later in a program.
These components allow a function to be fully defined and ready for use. However, for the function to actually run, it must be called by name within the program.
To be able to use a function it must have a name, called the function identifier. The function identifier can be any identifier of the programmers choosing as long as it follows the same identifier rules of variable identifiers. Functions are typically named with an identifier that uses a short word or two (separated using snake_case or camelCase) that describes what the function specifically does. The function identifier is defined in the function header.
A function might need inputs to be able to perform its operation. For instance a function may add two integers together, but to be able to add any two integers together the function must have a way to take in 2 inputs to add together. This is what the function parameters are for. The function parameters are variables declared in the function header to act as inputs to receive values the function will process. The parameters allow data to be sent to the function when it is used anywhere in the program. Since parameters are just variables they can be used in the function body as such, and their initial value will vary each time the function is used depending on the values passed when it is used. The function parameters are defined in the function header.
Functions may or may not produce an output. This output, if it exists, is called the return value – the actual data (if any) sent back when a function completes. The return type specifies what kind of data (if any) that return value is.
If a function does not return anything, then the return type is void and no return value exists. Otherwise the function is a value-returning function, which returns a single piece of data called the return value. The actual return value is computed as the code runs, but it must match the return type defined in the function header. The return type can be any data type as long as it is a single value of that type. This means arrays cannot be used as a return type, since they contain multiple pieces of data.
The function header is the first line of a function definition that specifies the function’s return type, identifier, and parameters. The function header is defined using the following syntax:
returnType identifier(parameters)
The returnType will be void if the function does not return a value, or a valid data type (such as int) if the function returns a single piece of data, which is the function’s output. The identifier is the name chosen by the programmer for the function, and the parameters are variables, separated by commas, that the function body can use within its set of instructions to produce different results when the function is called.
A function is simply code that can be run by name, and the function body is where that code is written. The function body is the set of instructions (a block of code) that execute when the function is called. It is enclosed in braces:
{ // function code }
Code written inside of the {} executes whenever the function is called by its name, allowing the same code to be reused without rewriting it. However, a block of code by itself has no name, so it cannot be used directly. To make it usable, it must be combined with a function header:
returnType identifier(parameters) { // function code }
This combination allows the code inside the braces to be easily referenced using the function’s identifier. The code inside the body can also use any of the parameters (variables) defined in the header, which receive their initial value when the function is called.
When the code in the body finishes executing, it either:
returns nothing (in the case of a void function) after which control returns to the point in the program where the function was called, or
returns a single piece of data (in the case of a value-returning function).
Both of these cases are described in detail below.
Together, the function header and body create the function definition – the complete code that defines a function. This definition is typically written below the main() function:
// preprocessor directives/namespaces int main() { // main function code } returnType identifier(parameters) { // function code }
This structure ensures that when the .cpp file is opened, the main() function is the first thing seen. Since main() is always the starting point of program execution, it is the natural place for anyone reading the code to begin. Then, if main() calls any functions that need to be examined more closely, the programmer can simply scroll down to view their definitions.
For a function to be used, it must be called. A function call tells the program to execute the code inside a function’s body. When a function is called, the program temporarily stops executing the code in the current function, jumps to the beginning of the called function’s body, executes its instructions, and then returns to continue execution where it left off in the calling function.
To call a function, the functions identifier is used, along with any arguments – the actual input values that are passed into the function’s parameters:
// preprocessor directives/namespaces int main() { // main function code identifier(arguments); // calling the function } returnType identifier(parameters) { // function body }
The identifier allows the program to find the correct function to execute, and the arguments provide initial values for the variables defined in the parameters.
However, this call is currently invalid. The compiler reads code from top to bottom, so it encounters the call identifier(arguments) before it encounters the function definition returnType identifier(parameters).... Because the compiler has not yet been introduced to the function, it generates an error:
error: use of undeclared identifier 'identifier' 8 | identifier(arguments); // calling the function | ^ 1 error generated.
The compiler does not know what the function’s identifier refers to, so the error is generated. To fix this the compiler must be told in advance that the function exists and will be defined fully later in the file. This is done by adding a function prototype above the main() function.
A function prototype is a declaration that tells the compiler a function exists and how it should be used before the function is fully defined later in the program. In other words, the function prototype defines the function’s usage to the compiler, but not the code that will be executed.
The function prototype looks the same as the function header, except it is not combined with the function body and ends with a semicolon (;). Function prototypes are usually placed above the main() function so that the function can be used throughout the program. In general, a function prototype follows the form:
returnType identifier(parameters);
The returnType, identifier, and parameters will be the same as the function’s header. However, the function prototype is placed above main() so that the function can be recognized and used anywhere in the program:
// preprocessor directives/namespaces returnType identifier(parameters); // prototype int main() { // main function code identifier(arguments); // calling the function } returnType identifier(parameters) // header { // function body }
The prototype establishes what is known as a function signature – a unique fingerprint made up of a function’s identifier and parameter types. The compiler uses this signature to verify that all function calls are valid.
Consider the function prototype:
void f1();
The compiler records its signature as:
f1()
Which includes only the function’s identifier and parameter types (none in this case) of the function. The compiler stores this signature in an internal table that it uses later to confirm whether function calls match a valid, declared function signature.
So now when the function is called:
void f1(); int main() { f1(); }
The compiler compares the signature of the call f1() with the table of recorded signatures to find a matching signature f1() from the prototype void f1(). Since they match, the compiler knows the function call is valid and that the function body will be defined later:
void f1(); int main() { f1(); } void f1() { /* code for f1 */ }
However, if the function is never defined:
void f1(); int main() { f1(); }
Then the compiler will fail to link the prototype to a definition since there is no definition to link the prototype to. This leaves the program with no code to run where the function calls occur, so the program has nothing to execute at each function call. This causes a linker error to occur because the program can not execute a function body that is not defined:
Undefined symbols for architecture arm64: "f1()", referenced from: _main in test-52d6ee.o ld: symbol(s) not found for architecture arm64 clang++: error: linker command failed with exit code 1 (use -v to see invocation)
To fix this error, the function definition must simply be added.
To fully define and use a function, three parts are required:
A function prototype at the top of the program (before main()),
A function call where the function is needed throughout the program, and
A function definition at the bottom of the program (after main()).
Together, these elements allow a function to be recognized, called, and executed correctly:
returnType identifier(parameters); // prototype int main() { identifier(arguments); // call } returnType identifier(parameters) // header { // function body }
A void function is a function that does not return a value when it finishes executing. When the returnType of a function is void, the function is a void function. Void functions are used to perform a task without needing to send a specific value back when the task is completed. They are useful for outputting data, modifying memory, performing internal calculations, or completing repetitive tasks – essentially anything that can be done that does not require a value in return.
For example, consider the function:
void outputMaxDamage() { // calculate max damage int level = 40; int maxDamage = floor(level * 64 / 320.0); // output max damage cout << "Level " << level << " causes " << maxDamage << " damage at maximum.\n"; }
When the function is called:
void outputMaxDamage(); // prototype int main() { outputMaxDamage(); // call return 0; } void outputMaxDamage() { // calculate max damage int level = 40; int maxDamage = floor(level * 64 / 320.0); // output max damage cout << "Level " << level << " causes " << maxDamage << " damage at maximum.\n"; }
The program stops executing main() and executes the body of outputMaxDamage(), which calculates the maxDamage based on a level of 40, and outputs the result to the terminal. Once the damage message is output the program returns to main() which then finishes execution.
Since no value needs to be returned – the calculated value is simply displayed on the terminal – a return type of void is appropriate for the outputMaxDamage() function. However, this function will always produce the same output ("Level 40 causes 8 damage at maximum.") because the level variable is defined inside the function and is always set to 40.
If the programmer were to move the declaration of level to main(), it still will not work:
void outputMaxDamage(); // prototype int main() { int level = 40; outputMaxDamage(); // call return 0; } void outputMaxDamage() { // calculate max damage int maxDamage = floor(level * 64 / 320.0); // output max damage cout << "Level " << level << " causes " << maxDamage << " damage at maximum.\n"; }
Now level is defined in main() but not in outputMaxDamage(), meaning it is out of scope for that function. This means that level can only be accessed in main() so when outputMaxDamage() tries to access it a compiler error is generated:
error: use of undeclared identifier 'level' 19 | int maxDamage = floor(level * 64 / 320.0); | ^
This happens because identifiers are only accessible from within the block of code in which they are created. outputMaxDamage() cannot see variables defined inside main(). This is known as a scoping issue, which can be solved by using parameters to pass values between functions.
In programming, scope refers to the region of a program where an identifier can be accessed or used. There are two types of scope:
Local Scope - Identifiers declared inside a block of code that can be accessed only within the block in which they were created.
Global Scope - Identifiers created outside of a block of code that can be accessed anywhere within a program.
Any identifier created inside of a block of code – such as a function, loop, or conditional statement – is called a local identifier, meaning it is local (only accessible from) that specific block of code. Meaning, when the block of code ends, the identifier is no longer accessible. For example consider the following snippet:
int main() { int i = 0; while (i < 2) { cout << i << endl; i++; } cout << "ending value of i: " << i << endl; }
This creates the variable i within the scope of main():

Since i is created in main(), it can only be accessed within the braces that define main(). The while loop is inside main(), so it can access variables declared above it in main()’s scope. The variable i can also be accessed after the loop in the cout statement since it was declared in the scope of main() and the cout statement is inside of main. But if i were to be declared inside of the while loop:
int main() { while (i < 2) { int i = 0; cout << i << endl; i++; } cout << "ending value of i: " << i << endl; }
Then i would only be declared in the scope of the while loop, not the entirety of main():

So the loop condition would now be using the i prior to being declared, and outside of the scope of i since i was created inside the while loop and the loop condition is outside of the while loop’s body in main(). Also when the loop ends i would go out of scope and could no longer be accessed by the cout statement so 2 compiler errors would be generated:
error: use of undeclared identifier 'i' 7 | while (i < 2) | ^ error: use of undeclared identifier 'i' 13 | cout << "ending value of i: " << i << endl; | ^ 2 errors generated.
Local scope also applies to functions in exactly the same way – each function has its own local variables that cannot be seen by other functions. When an identifier is created inside of a function it is a local identifier that can only be used inside of the function. If the identifier is used in any other function then a compiler error similar to the above 2 will be generated.
Take for example the state outputMaxDamage() was left in:
void outputMaxDamage(); // prototype int main() { int level = 40; outputMaxDamage(); // call return 0; } void outputMaxDamage() { // calculate max damage int maxDamage = floor(level * 64 / 320.0); // output max damage cout << "Level " << level << " causes " << maxDamage << " damage at maximum.\n"; }
Each function – main() and outputMaxDamage() – have their own local variables:

Since level is local to main() it can only be accessed inside the braces that define main(). The function outputMaxDamage() is outside of this scope, so when it tries to access level the compiler generates an error.
Anything with global scope can be accessed from anywhere in the program. Because of this, globally scoped items must be defined or declared at the top of the program, before any function definitions. These are known as global identifiers – identifiers that can be accessed anywhere within a program.
Global identifiers include commands contained within libraries and functions after their function prototypes have been declared. Since these are defined at the top of the program, prior to any function definitions, they are available for use throughout the entire program and are therefore considered global in scope.
Since local variables can only be accessed within their scope there has to be a way to pass their values to a function that is called. For this parameters are used. The parameters of a function are variables declared in a function header to act as inputs to receive values the function will process. They provide a way for the calling function to give input to the called function when it is called. There are 2 types of parameters:
Formal Parameters - Variables and constants defined in the function heading to be used in the function body.
Actual Parameters - Actual values passed to a function when it is called.
Formal parameters are defined in the function heading and are used in the function body:
returnType identifier(formalParameters) { // body }
They are the actual variables that are filled with data by passing actual parameters when a function is called. So for the following function:
int add5(int i) { return i + 5; }
The variable i is a formal parameter that is used in the function body to add 5 to, then return the updated value from the function.
Actual parameters are the values, typically local to the calling function, that will be passed to the function call to fill the formal parameters with data. So in the following call:
int main() { int data = 10; int result = add5(data); }
The variable data is the actual parameter to the call to add5() which fills the formal parameter i with the value 10 for this call to the function.
It is important to note that if a function has multiple parameters:
int getValue(string prompt, int min) { ... }
Then the actual parameters must be passed to a call to this function in the exact same order they were defined in the header:
getValue("Integer: ", 0);
This way the actual parameters clearly show what formal parameter the values are being placed in. If they are passed in the incorrect order:
getValue(0, "Integer: ");
The program has no way to tell the 0 is meant for min and "Integer: " for prompt (which becomes more apparent as more parameters are added), so a compiler error is generated:
error: no matching function for call to 'getValue' 9 | getValue(0, "Integer: "); | ^~~~~~~~
Which can only be resolved by passing the actual parameters in the proper order.
When void functions use parameters it is to modify the outcome of the operation the function is performing. However, since void functions do not return anything the parameters do not affect any values returned.
Going back to outputMaxDamage() parameters can be applied so that the level can change per function call which will affect the maxDamage that is calculated and output. The level can be passed as a parameter:
void outputMaxDamage(int level); // prototype int main() { outputMaxDamage(40); // call return 0; } void outputMaxDamage(int level) { // calculate max damage int maxDamage = floor(level * 64 / 320.0); // output max damage cout << "Level " << level << " causes " << maxDamage << " damage at maximum.\n"; }
Now, when the function is called an integer must be passed as an actual parameter which fills the formal parameter level with the value 40. This affects the outcome of maxDamage which receives a different result each call based on the value passed as an actual parameter.
Currently when outputMaxDamage() finishes executing the value of maxDamage is lost as it is local to the function, which goes out of scope when the function finishes executing. There is no way for main() to use the value of maxDamage such as in a further calculation where the actual amount of damage is calculated based on the maxDamage. To use the value of maxDamage back in main() the value must be given back to main when the function finishes executing, which can be done with value-returning functions and reference parameters.
To give a single value back to a calling function from a called function when it finishes executing a value-returning function can be used. A value-returning function is a function that returns a single value of a specific type when it finishes executing. To return the single value the return operation is used:
returnType identifier(parameters) { return returnValue; }
First, the returnType is changed from void to any data type (i.e. int, double, string, etc.). The type specified as the returnType then determines what type of data can be returned as the returnValue. The returnValue is then any value of returnType as long as it is a single value of that type (so no arrays). Once the function is called:
... { identifier(arguments); }
The body of the function executes, presumably computing the returnValue, and when the body finishes executing the returnValue is returned back to the function that performed the function call. So essentially the call resolves to the returnValue:
... { returnValue; }
Which can then be saved in a variable, used in a calculation, used as an actual parameter to another function call, or used in an output statement:
variable = identifier(arguments); // value saved // -or- variable = 10 * identifier(arguments); // used in calculation // -or- function(identifier(arguments)); // parameter to function call // -or- cout << identifier(arguments); // output statement
So the only difference between a void function and a value-returning function is that a value-returning function returns a value using the return keyword. A value-returning function changes the returnType to some type, and then uses the return keyword to return a piece of data of that type. A void function does not. Everything else is the same between the two function types.
Going back to outputMaxDamage() the maxDamage may need to be used back in main for further computations, such as computing how much actual damage may be caused when the attack is done. So the function can be changed from a void function to returning an int:
int calculateMaxDamage(int level); // prototype int main() { const int MAX_DAMAGE = calculateMaxDamage(40); // call // use MAX_DAMAGE to compute further return 0; } int calculateMaxDamage(int level) { // calculate max damage int maxDamage = floor(level * 64 / 320.0); return maxDamage; }
The function is changed to return an int because the maxDamage that needs to be returned is of type int. The maxDamage is calculated as usual then returned from the function to the caller using the return keyword so that the caller can use the maxDamage further. The identifier is also changed from outputMaxDamage to calculateMaxDamage since the function no longer outputs the maxDamage, rather it returns it. Once the function finishes executing maxDamage’s value is returned, which is then saved in main() to be used in further computations.
Function - A named block of code that performs a specific task.
User-Defined Function - A function the programmer creates to perform a specific task.
Function Identifier - The name given to a function.
Parameters - Variables declared in a function header to act as inputs to receive values the function will process.
Return Value - The actual data (if any) sent back when a function completes.
Return Type - The kind of data (if any) a return value is.
Function Header - The first line of a function definition that specifies a function’s return type, identifier, and parameters.
Function Body - The set of instructions (a block of code) that execute when a function is called.
Function Definition - The complete code that creates a function.
Function Call - Tells the program to execute the code inside a function’s body.
Arguments - The actual input values that are passed into the function’s parameters.
Function Prototype - A declaration that tells the compiler a function exists and how it should be used before the function is fully defined later in a program.
Function Signature - A unique fingerprint made up of a function’s identifier and types of parameters.
Void Function - A function that does not return a value when it finishes executing.
Scope - The region of a program where an identifier can be accessed or used.
Local Scope - Identifiers declared inside a block of code that can be accessed only within the block in which they were created.
Local Identifier - Any identifier created inside of a block of code – such as a function, loop, or conditional statement.
Global Scope - Identifiers created outside of a block of code that can be accessed anywhere within a program.
Global Identifiers - Identifiers that can be accessed anywhere within a program.
Formal Parameters - Variables and constants defined in the function heading to be used in the function body.
Actual Parameters - Actual values passed to a function when it is called.
Value-Returning Function - A function that returns a single value of a specific type when it finishes executing.
Value Parameter - Formal parameter that receives a copy of the value of the corresponding actual parameter.
Reference Parameter - Formal parameter that receives the address of the corresponding actual parameter.
To Be Added Later