Functions

This will be a discussion of functions in C++. There will also be notes on the use of reference parameters.

Functions

All programming languages have functions. They can be called different things. Subroutine, routines, procedures and functions all describe the same thing. A function is a piece of code that is run by itself. Control is transferred to it from the main program and control returns after the function is completed.

Digression

The control flow of a program is the sequence of instructions that it executes when it runs. The default order in C++ is to start at the first statement in the main function and continue from the top to the bottom. We have already seen some control statements that can alter the control flow.

Functions can take arguments and can return results. The general form of a function in C++ is

return_type
func_name(type1 arg1, type2 arg2,...) {
     // code for the function
} // func_name

A function can have no arguments. In C++, a function is called by using its name followed by a parenthesized list of arguments.

// Convert Fahrenheit to Celsius
float
ftoc(float ftemp) {
   float ctemp;
   ctemp = (((ftemp -32 ) *5) / 9.0 );
   return(ctemp);
} // ftemp The return type is float. This means that anywhere a float can be used, a call to this function can be used. Its name is ftoc and it takes one argument, a floating point number. At the end, it returns a float value. In between the braces is the code that implements the function. Variables can be declared inside the function that are local to the function. This means that the variables, like len, are only used inside the function. This variable len would be different from another variable len used somewhere else in the program. A few other comments. If the function doesn't return anything, the return type is void. It is possible to make a function that takes an unknown number of arguments, but this works in a very different way.

Some details about the return statement. If the function doesn't return a value, the return statement is optional as a return occurs when the end of the function is reached. But a return statement can be used if you want to exit the function early. In this case, the form is just return; If there is a value to return, there are two forms. Both forms work exactly the same.
return expr; return (expr);
A function can return only one value.
About function calls and how they work.
A typical function call looks like this
new_temp=ftoc(32.0);
When this is executed, the control transfers

from where the call to ftoc is in the code to the beginning of the code in the ftoc body. The variables described in the function header are set with the values in the function call. The variables in the function header are called the formal parameters. The values in the call to the function are called the actual parameters. The formal parameters are like local variables in the function code. The code in the function is executed in the usual order. When a return statement is executed, control goes back to the main program just at the point it left. If the function returns a value, then it is as if that value was put in place of the function call. So if the actual call was like this

temp = ftoc(32.0; // note the use of an float constant
Then control starts at the top of the ftoc function. The formal parameter is a float. All the arguments to the function are evaluated before the code is executed. But C++ doesn't guarantee that they are evaluated in any particular order. So don't assume it is left to right. Then the formal parameter, ftemp is set to the value 32.0. The arithmetic is done using the value in ftemp. The result is stored in the local variable ctemp. In this case, the value is 0.0 The return statement send the 0.0 back to the calling function. So the program acts as if the line above was changed to temp = 0.0; Then the program goes on from that point as if it had never called the function.

Function declarations are used in cases where the compiler needs to know about the function before it sees the code. The compiler needs to know about the ftoc() function inside main so it can tell if the call is legal or not. So we put the function declaration at the top of the file. The declaration looks just like the definition except for two things. The body code is missing and the formal parameters names are not needed. A common use of declarations is in header files so the compiler can know about functions that may be in other files and which may not have been written yet.

Functions in C++ may have default arguments. For example, void str_print(char * str, int length = 10);

This declares a function with one default parameter. If the function is called with only on parameter, like str_print("hello"); Then the value of the second parameter is set to 10. If a value is supplied, that is used rather than the default. The default parameter is mentioned only in the declaration, not in the definition. Also, if there is a default parameter, then all parameters after that one must also have defaults.

Parameter passing techniques

All parameters in a C++ function are pass-by-value. There are two major ways information is passed to a function. One is pass-by-value. This means that the expression that is the parameter is evaluated and the result is stored in a local variable. This value is a copy of the original. In this example from the code,

rtc=ftoc(rtf);
A copy of the value of rtf is stored in the local variable ftemp. If we were to change the ftoc() function to add a line

ftemp=0.0;
this would have no affect on the value of rtf. This is because ftemp is a local variable and is not related to rtf.

The other main method is pass-by-reference. Instead of a copy of the value being passed, a reference to the actual parameter is passed. This means that if the formal parameter is changed, so is the actual parameter. There are times when this is what you want and the details are in the next section.

Recursion is supported by C++. This allows a function to call itself. There are many problems, for example, linked lists, where recursion makes the program easier to understand. Key to the ability to do recursion is the fact that the formal parameters are local variables. So each time a function calls itself, there is a new copy of the variables. It is important to remember that the computer views all function calls the same, even if it is calling itself.

Reference Parameters

If a function is set to be call-by-value, which is the normal case, then changing the formal parameters has no effect on the actual parameters. For example,

int outside_a=8;
cout << "before: " << outside_a << endl;
cbv(outside_a);
cout << "after: " << outside_a << endl;

where the function cbv() is defined as

void
cbv(int inside_a) {
   inside_a=7;
} // cbv

We would see printedbefore: 8 after: 8 And this is what we often want. But sometimes, we want to change the actual parameter when we change the formal parameter. If we use pass_by_reference, then when we change the formal parameter, the actual parameter is changed. So in this example,

int outside_a=8;
cout << "before: " << outside_a << endl;
pbr(outside_a);
cout << "after: " << outside_a << endl;

where the function pbr() is defined as

void
pbr(int& inside_a) {
   inside_a=7;
} // pbr

We would see printedbefore: 8 after: 7
the corresponding code if we didn't have pass-by reference is

int outside_a=8;
cout << "before: " << outside_a << endl;
cbv(&outside_a);
cout << "after: " << outside_a << endl;
where the function cbv is defined as
void
cbv(int *inside_a) {
   *inside_a=7;
} // cbv

We would see printedbefore: 8 after: 7
This works because we passed the address (a pointer) of outside_a and when we changed *inside_a, this changed the thing it pointed at, which is outside_a. Common uses for pass-by-reference are to update something when we want the return value to tell us if it worked or not. Or when we need to change or return several value. It can also be used even when we don't want to change the argument. If the thing we are passing is large, some kind of structure for example, pass-by-reference is better because pass-by-value makes a copy of the who structure, which can take time and uses memory. Also, you may have functions where you want to change the parameters sometimes and not others.

The pass-by-reference method is C++ is much easier to use and get right than the pointer method. If you forget the ampersand on a call, the function will still work but not do what you expect. This can be hard to debug. References can be used outside of function calls. In this case they are a kind of alias of another variable. Look at the following declarations:

int normal_a=42;
int *pointer_a = &normal_a;
int & ref_a = normal_a;
ref_a has become another name for normal_a. To use pointer_a to change normal_a, you have to do *pointer_a = 56; But you can use ref_a = 56; pointer_a can be changed to point to another integer, but ref_a is always attached to normal_a.