Divide and conquer!
Latin Idiom
You are already familiar with the main() function (_tmain() in Visual Studio dialiect). This is a special function that is automatically executed when program starts. As your program gets bigger, the main() function grows and it gets harder and harder to understand the code. You can fight the complexity by creating your own custom functions. You break apart your code and make it easier to understand.
As with everything else in C/C++, you must define a function before you can use it. Let’s take a look at this example:
// a function that calculates factorial
int factorial(int n) {
int res = 1;
int ii;
for(ii = 1; ii <= n; ++ii) {
res = res * ii;
}
return res;
}
This function calculates a factorial of the integer number. After you defined a function, you can call it with different actual parameters:
int main(int argc, char** argv) {
int z;
z = factorial(10);
cout << “Factorial of 10 is “ << z << endl;
cout << “Enter a number:” << endl;
cin >> z;
cout << “Factorial of “ << z << “ is “ << factorial(z) << endl;
return 0;
}
The function definition consists of the following items:
- the function name (similar to variable names rules apply: function name must start from a letter, contain only letters, digits and underscore ‘_’ and be unique);
- the function input parameters definition (what functions expects);
- the function return type (what should be expected back);
- the function implementation (body).
return_type function_name(input_parameters_list) {
function_implementation
}
Our factorial function from the example above has name “factorial”, it requires one integer input parameter (“int n”) and returns an integer (“int”).
A special return type “void” is used to indicate that a function has no return parameters:
void print_result(int step, float res) {
cout << “On step “ << ii << “ the result is “ << res << endl;
}
This function has two input parameters: first of type int and second of type float. When you call a function you must make sure that actual parameters have correct types:
print_result(10, 0.345); // correct print_result(11, “some string”); // incorrect: compiler error “type mismatch”
A special keyword “return” is used inside the function body to stop the function execution immediately and return the value to the caller. A non-void function must return a value on all execution paths. The following function will generate compiler error: if a is equal to b then there is no “return” value:
int foo(int a, int b) {
if(a < b) {
cout << “a is less than b” << endl;
return -1;
} else if(a > b) {
cout << “a is greatre than b” << endl;
return 1;
}
cout << “a is equal to b” << endl;
// compiler error - no return value!!!
}
A void function can use return statement to exit from the function early w/o returning anything:
void bar(int x) {
if(x <= 0) {
return; // if x <= 0 then function stops here
}
cout << “The x is positive” << endl;
}
When you are doing step-by-step debugging of a program with functions, you can either step over the function call or step into the function call to execute a function step-by-step with actual parameters for this call.
Large programs contain multiple files. To make defined functions from one file visible in another file, programmers separate function declaration and function implementation. The function declaration is placed in a header file (which usually has .h extension):
// File factorial.h int factorial(int n);
and function implementation in the source file (which usually has .cpp extension):
// File factorial.c
#include “factorial.h”
// a function that calculates factorial
int factorial(int n) {
int res = 1;
int ii;
for(ii = 1; ii <= n; ++ii) {
res = res * ii;
}
return res;
}
The include command is used to insert into the current file the content of included file “as-is”. Now you can use your function anywhere!
// File program.cpp
#include “factorial.h”
int main(int argc, char* argv[]) {
cout << “Factorial of 10 is “ << factorial(10) << endl;
}
You probably noticed that we’ve used include command before to load function definitions from the standard C/C++ libraries:
#include <iostream> #include <stdlib.h> #include <math.h>
Note that we used quotes to load our custom header file
#include “factorial.h”
and less/greater brackets to load standard C/C++ header files. The reason for this difference is the C/C++ compiler algorithm for locating the correct file on disk. For now, just follow this convention: custom header files from the same project should be included with quotes and standard header files from C/C++, Windows, etc. should be included with less/greater brackets.
The process of modifying the source code to make it more readable and more maintainable is called re-factoring. Separating the code into re-usable functions and breaking apart big files into smaller files is one of the easiest ways to re-factor your code.
Exercises
1 ) Re-factor your Fibonacci program and create a function for calculating the Fibonacci number.
2) Re-factor your GCD (greatest common divisor) program and create a function for calculating the GCD.
3) Re-factor your factorial program and create a separate header/source files for the factorial function. Use “Project” / “Add File” menu in Visual Studio to create new files. Don’t forget to add new files to git before committing your changes!