C++ Functions (revisited)

Earlier, we introduced the basic concept of a function so you could make some sense out of the example code we used to test out the tools!. We need to look a bit closer at these function things to see how they work in more detail.

The basics

As we saw earlier, a function is just a named block of statements. Normally, but not always, functions will return a value that we can use in our calculations (think about how a sqrt function would work).

Here is the basic way we define a function:

float my_sqrt(float x) {
    ...
}

In this chunk of code we have defined the mane of the code block (my_sqrt) the kind of value it will return (float) and we have defined what kind of parameter we will provide as we ask this code to do real work (float again). The variable name x is a name that will be known only inside the box. The code that wants to calculate the sqrt using my cool function will not use this name.

In fact, here is how we could activate this chunk of code:

float var1 = 10.0'
float var2;
...
var2 = mysqrt(var1)

Here, we defined two variables, both set up to hold float numbers. We initialized one of them, but not the other. This is fine as long as we put something meaningful into var2 before we use it for any calculations.

We called the my_sqrt function and provided a value for it to chew on. We do not know - and do not care- how my_sqrt does it’s job, we are only interested in the result.

This is the most common kind of function - but not the only kind!

Focusing on the parts

We have three function parts to deal with:

  • The name of the function - easy!
  • The return value
  • The parameters

The pattern you use when building a function looks like this:

return_type function_name(parameters {
    code that does stuff;
    return (if needed)
}

Each of these need to be looked at in detail!

The name of the function

You come up with a name that makes sense. Use a name that will help the user of your function figure out what it does. This is more important that you might suspect. We want to create programs that explain themselves to a reader of your code (who may be your boss!)

The return type

In our example, we picked a data type for the value we plan to return to the caller of our function. However, not all functions need to return a value. It is perfectly OK to create a function that does work, and returns nothing to the caller. Such a function might look like this:

void do_work(int var1) {
    ...
}

here, the keyword void means we will return nothing to the caller. The code to activate this function is not the same as other examples. Here is how we would activate this example function:

dowork(5);

Since the function wanted a number to work with, I need to provide it!

If the function needs to return a value to the caller, the function looks like this:

float my_function(float var1) {
    ...
    return float_value;
}

In this function, the return value is whatever you place on the return line. If you fail to include such a line, the compiler will complain!

The value you return must be of the same type as the type you put on the function definition.

When you return the value, the code must be expecting the value at the point where the function was activates (called). This can be in the middle of an expression, or even inside the call to another function. Here are some examples:

float x;
x = sqrt(4.0);
x = 5.0 + sqrt(10.0);
x = sqrt(my_sqrt(5));

The first example is a simple assignment statement. The value returned by the function is simply placed in the named variable.

In the second example, the function returns a value which is combined with the rest of the expression before finally putting the result in the named variable.

The last example is a bit more complex. We call the inner function (my_sqrt) and get a value back. That value is passed on to the outer function so it can do its work. When that second value is returned, it is placed into the named variable. Easy, huh?

Parameters

Parameters are a bit more complicated because there are two ways we may want to work with them. To understand this we need a bit of background.

Referencing a variable

Any time you see a variable name on the right side of an assignment, or in a parameter list, we are referencing the variable - meaning we look into the container and pull out the current value stored there and use it. The location of that value is not important. If we pass that value to a function, the function has no idea where it came from - it just uses the value and presses on!

But there is another way to do this!

Suppose, instead, we tell a function where the variable lives in memory. Now the function can access the value stored there, but it can do another thing as well. It can change the value stored in that variable to something else. This was not possible in the first scheme! Sometimes we want to allow this to happen. But to make it work we need to change the notation:

float my_function(float &param1) {

    ...
    param1 = 4.0;
    ...
    return float_value;
}

In the body of this function, we put a new value into the parameter variable . In our first scheme this would be known only to the function itself. However, since we added the & in front the parameter name the function is actually working with the caller’s variable (whatever that was). Now the caller feels the effect of this change:

float var1;
var1 = my_function(var2);
cout << var2 << endl;

The function wants the name of a variable in the parameter part, and it would be a mistake to provide a literal value instead. When the function finishes work and the caller gets control back, whatever was in var2 is now

Note

The name you give to the parameter when you create the function is known only inside the code of the functions. When the caller uses this function, they will provide the name of a variable they know about. The function will not know what that name is, but it will know where it lived in memory, and when it refers to its internal parameter name, it will use the same memory location used by the callers variable. This is called call by reference since the referenced variables address is provided for the function to use. The other scheme is called call by value since the function is provided only with the value.

It seems confusing when you start working with this, but you will get the hang of it. If oyu do not want a function to mess with your variables, do not use call by reference!