C++ L05 – Functions

Functions are pieces of code that do certain things based on given arguments. When writing programs, you will start to notice patterns in your code, things that you repeatedly do. Instead of copying and pasting code here and there, you simply wrap this code as a function and call that.

Not only this saves you a lot of typing, but imagine if for some reason you want to change a pattern (like a sneaky typo); you would have to go hunting for every instance. If instead, you have defined your reusable patterns as functions, you can change it once and work everywhere.

Code Duplication is the Mother of all evil in Development

The way to define a function in C++ is this:

return_type function_name(arg1_type arg1_name, ... , argN_type argN _name){
    code;
    return return_value;
}

The first line is the function prototype. It first states the return type of the function. If the function doesn’t return anything, we use void as the type. Next is the function name. Next, in the parenthesis we state the arguments that the function accepts. Each argument has a type and name. We can have as many arguments as we like, including zero. After that we define the body of the function with the code that will run when we call the function.

Return value is the result of the function and it must be convertible to return_type, otherwise the compiler will fail to compile your program. A function not returning anything (void return type), is not required to have a return expression. Using the return keyword, we leave the function call immediately and return the result to the call site. Return is to functions what break is to loops.

Functions have a single return argument. You can walk around this by using pointers (we will check them out later), but generally it is better to only do one thing in the function and return a single argument.

The way to call a function is similar to mathematical functions. Example code for definition and calling of functions:

//definition
int abs(int x){
    x = x > 0 ? x : -x;
    return x;
}

int main(){
   int A, B;
   std::cin >> A;
   B = abs(A);//call
   std::cout << "|" << A << "|= " << B << "\n";
   return 0;
}

The x argument, is actually a variable defined at the scope of the function and it is initialized with the first argument (and only in this case) that we pass to the function call. That means, that changes to this argument are not "passed" outside the call, since it is a copy occupying different location in memory. You can notice this in the above code; we alter the variable x inside the abs function, but A from main remains the same. Types of provided arguments at the call site must be convertible to the declared arguments.

You can of-course call functions within other functions. Since main is a function, albeit a special one, we are doing this in the above code by calling abs. Functions can call any other function, or even themselves(recursion). You can call a function anywhere that a value is required provided that the return type of the function is convertible to the type required.

Mini-boss

what does the abs function do?

Functions are a great code organization unit. Keeping your code tidy and clean is very important, as you will discover if you program long enough. External libraries and even standard libraries, that are key to effective development, are essentially collections of functions (among other things).

A good development practice is to have placeholder implementations for functions that simply work and later rewrite, refactor and optimize them. That way you can keep going, without having to stop your flow, while simultaneously avoiding the problems I've mentioned early about copy and pasting code.

To use a function, like any other named entity in C++, you have to at least declare it somewhere BEFORE. The way to do that is either by defining the function before the call, or by declaring it's prototype. Header files that we include in most programs, are just list of declaring things such as function prototypes. Example below:

int abs(int x);

int main(){
   int y = abs(-6);
   return y;
}

Simply declaring a function can get you through the compiler step. However, the linker must find the definition somewhere, either in your object code that was generated from your files through the compiler, or in the linked libraries. If the linker fails to find the function you are referring to, it will assert an 'undefined reference to something error'. In that case, you probably forgot to define it, or forgot to pass the required library arguments to the linker.

You can declare a function prototype as many times as you like, but all declarations must be the same. The definition can be anywhere in the linker provided code, in the example above we could define the abs function after/below main.

Random

In most games and some non-game software, the ability to change things around a little using randomness is essential. Computers are deterministic. Some could argue that the entire universe is deterministic, so true randomness is impossible, but if we are not talking cryptography it is not really essential.

With computers you can get pseudo-random sequences of numbers. The random numbers that are generated are based on a seed value. For the same seed value, you will get the same sequence. This is handy for debugging.

In C++ we can generate random numbers like this


#include 
#include 

int random_int(int low, int high){
    return low + (rand() % (high - low + 1));
}

int main(){
   srand(time(NULL));

   int y = random_int(1, 11);
   std::cout << y;
   return 0;
}

We can get (uniformally) a random number in the set [0, RAND_MAX] (where RAND_MAX is a constant) using rand(). With some math we can map this set into any set we want. A transformation like this is the implementation of random_int which returns a random positive number in the set [low,high).

With srand we seed the Random Number Generator. We provide the integer returned from time(NULL), which is the numbers of seconds since the Unix Epoch (1/1/1970). That way every time we restart the program, we get a different seed and thus a different pseudorandom sequence from rand().

Mini-boss

Compile and link the code above and make the definition of abs take a single line of code (not counting prototype, opening and closing brace)

Boss

Go to the previous chapter and refactor the code so it organizes the code in functions. The resulting code should look something like the game loop pseudocode. It may help you to convert some variable into globals.

Author: Lefteris Chatzipetrou

Lefteris is a Co-Founder and CTO over at HAM Systems. He has wide experience in electrical engineering including electronics and embedded systems, mobile, web services and video games development.

Leave a Reply

Your email address will not be published. Required fields are marked *