Chapter 5 : Functions

Chapter 5 : Functions

Contents

5.1. Motivation: the need for functions.

The basic property of functions is that they enable us to break a program down into a number of smaller units. There are several different reasons why programs should be broken down in this way. Different users attach different importance to these reasons; some of the considerations below may be considered irrelevant by some practitioners.

If a solution has been created for a particular sub-problem (an implementation of the solution involving means for storing the related data items, and the means of accessing them in an appropriate way) may be useful in more than one problem. An example is the sorting of a number of items into a particular order; sorting is a basic operation which is needed at many points in many computer problems.

Units which solve frequently occurring sub-problems can therefore be re-used in appropriate places in a variety of larger problems.

  • (i) They may be used in order to save effort in re-solving that particular problem, and in re-programming the solution.
  • (ii) They may be used in order to produce better quality systems, on the assumption that the solution being re-used was written and tested (by someone else) to a high quality standard when it was originally written.

    In this C++ course we teach just the programming techniques needed for producing useful re-usable code; we are not emphasising just WHY you may choose to break down your problem into these particular units. We need what are called "functions" and "structures" in C++; in the next course you will learn techniques for using functions and structures as the basis of an object-oriented approach to the design of programs; they can be used in other design methodologies too.

    In this section of the course, we teach the techniques for implementing functions. For those who have used other programming languages, the concept of a "function" in C++ corresponds to a "procedure" or "subroutine" elsewhere.

    5.2. A simple example of a function

    The parentheses following the function identifier in the function calls in the main program indicate that "dothis" and "dothat" are identifiers representing functions to be executed. The functions may, of course, be called as many times as you wish within the program.

    5.3. Declaration versus definition

    For each function the compiler needs two distinct items of information.

  • (i) A declaration, to define (to tell the compiler) what a call of the function will look like, and the identifier to be used.
  • (ii) A definition, to define exactly what is to be done (the actions to be executed) whenever the function is called.

    This should be compared with the declarations of variables, where again there are two distinct aspects to any declaration, one to inform the compiler about the type and identifier for compile-time, and one to arrange to claim space at run-time.

    In the above simple example, declaration and definition are combined.

    These two parts (declaration and definition) of a function can be separated in C++ as in the example

    The first declaration "void dothis();" is to warn the compiler what to expect as calls of the function, for example by introducing the identifier to be used. The compiler will not now be surprised to see the calls of the function when it encounters them in the main program. Although the compiler knows that dothis is the identifier of a function, not of a variable, it still requires parentheses after the identifier in function calls in the program.

    The later definition "void dothis() { ... }" with executable statements within curly braces will define between those curly braces the code to be executed whenever the function is called. It will involve executable C++ code.

    5.3.1. The function declaration

    The function declaration needs to inform the compiler of

  • 1 Some functions will return a result, some will not. Functions to compute the square root of a number will return a numeric result for use in the program requesting the square root. A function to print output will not return a numeric value.
  • 2 The identifier of a function follows exactly the same rules as identifiers for variables. It must not clash with the special words of the language.
  • 3 We will see later that we will sometimes need to pass values into functions when we call them. With the "square root" example above, each time we call it we must give the value of the number whose square root we require. The number which we pass to it is sometimes called a parameter and sometimes an argument.

    Examples of function declarations

    A function to compute the square root of a given value.

    A function to compute the larger of two integer values.

    A function to print an error message involving an integer value.

    Examples of function calls

    The above functions would be called from elsewhere (perhaps from the main program, perhaps from another function) by statements such as the following.

    Square root would take a "double" parameter and return a double result.

    We would normally expect to use the returned result as a value.

    Maximum of two integers

    Reporting an error

    The syntax of a function declaration

    A function declaration consists of

    The returned type may be any ordinary C++ type such as int , float and so on, or the type void if no value is being returned to the main program.

    In some older programming languages, the word "function" was used only when a result was to be delivered; the word "procedure" or "subroutine" was used is there was no result to deliver. In C++ all such objects are called functions.

    5.3.2. The function definition

    The function definition needs to inform the compiler of the actions to be taken when the function is called. In the declaration we described only the types of the parameters. The compiler needed this information to enable it to handle the parameters correctly wherever the function is called. In order to describe the actions to be taken when the function is called, we need to identify each of the parameters, so that we can refer to them and use them from within the code which forms the body of the function.

    The "max" function

    We want a function to deliver the larger of the values of the two values given as parameters.

    The "return" keyword acts both to specify the particular value to be returned by the call of the function, and to cause dynamic exit from the function.

    If the function returns type void we leave the function using the statement

    with no value specified. If the function returns a non-void type, the return must be followed by a value which the compiler can force into the required type.

    Notice that we do not need an "else" statement in this example because of the dynamic exit caused by the "return i;" statement.

    The "error" function

    We want a function to print an error message and then abandon the program.

    In this example, the "exit" causes the whole program to terminate; if we merely wished to report the error and continue the program, we would use a

    statement with no value given.

    If the function has its delivered type declared as void its code does not return values, and the function is left by using

    The exit function

    Convention with the exit function is that

    The UNIX shell can use the value returned by a terminating program to determined whether it terminated successfully or not.

    The Ceilidh system will, in some exercises, require you to use the "exit" function to indicate errors in the data.

    The "sqrt" function

    We need to calculate the square root of the given parameter value, and return it. Please forgive the crude method!

    The syntax of a function definition

    A definition consists of

    5.4. Returned types

    The compiler needs to know the type of the value to be returned by the function so that it can be treated correctly in the call of the function. If no value is to be returned (cf "error" or "dothis" above) then the return type is given as "void".

    The particular value to be returned is specified by the line

    With a void function write simply

    The compiler will do type conversions where necessary. If the function declaration says that it returns a float and the function includes "return 1;" then the compiler knows to sort this out.

    5.5. Parameter types

    The parameter type sequence in the declaration and the definition must agree. The compiler will make the necessary type conversions to the parameters and to the delivered result in a call of the function. Again, the compiler will perform type conversions if the function declaration specifies a float parameter, and if the call includes an actual integer parameter.

    The parameters in the function definition are called the formal parameters. The parameter values supplied in calls of the function are called actual parameters.

    5.6. Default parameters

    A function defined with, say, 3 parameters can be called with less than three parameters if default values have been set. Default values are set using what looks like initialised declarations for the formal parameters.

    A possible example might be

    This could now be called as

    to find the maximum of the three values "p, q, r" or as

    in which case the last (third) parameter in the definition assumes its default value; we effectively get the maximum of the first two parameters.

    A drawback of using this technique is that errors in giving the wrong number of parameters to a function may not be detected by the compiler.

    Another example could be written as in

    The function can then be called as in

    5.7. Function overloading

    You can use the same function identifier for more than one function, providing that the compiler can determine the particular one to be chosen by the types (and number) of its parameters.

    The calls might be

    The compiler would then look at each call of the function, and determine from the types of the parameters which of the definitions was to be invoked.

    The strict rules to define what parameter types can be distinguished are very complicated; the best advice is just to make the difference very obvious!

    5.8. Program structure

    The main program and function definitions can appear in any order in your program. It is normally clearest to read and understand if the main program appears before the function definitions. Reading the main program gives an overview of the sequence of operations.

    Function declarations MUST appear before the function is called. They are usually put together before the main program.

    5.9. Local variables

    We can declare local variables at the start of our function code if we require additional variables for computations within the function. The declarations appear after the opening curly brace, just as they do in the main program.

    If a local identifier clashes with a global constant, or with the name of another function, then that global constant or other function becomes inaccessible within this function. The local object will be referred to by the identifier.

    5.10. Parameters called by value

    When a function is called, the values of the (actual) parameters at the point where the function is called are calculated, and passed to the function definition for execution. Within the function, the parameters act like variables, initialised to the value of the corresponding actual parameter at the call, and their values can be changed by ordinary assignment.

    Thus if a function definition is

    the identifier "number" can be considered as a local variable to the function. Changing its value inside the function as shown in the above example has no effect on the outside world.

    If the call of the function is

    the value of the variable counter in the calling program will not be changed. We could also call the function by

    which would pass the value 23 to the formal parameter. This way of passing parameters is referred to as "passing by value".

    There are some cases where we wish to change the value of a variable in the world from which the program is called; the use of a "swap" function to interchange two integer variable values is an example. We wish to pass to the function the names of two variables as parameters, and require the function to interchange the values in the two named variables. These are variables of the calling program, not of the function. We would want to call the function as in

    If we declared it as

    then the above call would not have any effect on the world outside the function. It would interchange only the values of the internal variables "p" and "q", not the program variables "p1" and "p2".

    To have the effect we require we must change the declaration to

    and the first line of the definition to

    If the function parameters are shown just as normal, without the "&" symbol, the only effect of the function is to interchange the two "local" variable values. To ensure that the actual variables whose identifiers are passed as parameters to the call of the function are swapped, the "&" sign shown in the example is necessary. The call will now interchange the values of the main program variables named as arguments. This is referred to as a parameters being "called by address" or "called by name" or "called by reference".

    A mathematical example might be a function to solve quadratic equations (non-mathematicians don't panic ...). The input to the function is three float values ("a", "b" and "c"), and the output is two float values (the roots, "x" and "y"). We cannot deliver two values directly as the result of a function. Instead we use two address parameters, so that the function specification is

    and a call might be

    The values of roots will appear in "x1" and "x2".

    The definition might be

    A less mathematical example; we are given a number of bicycle wheel rims and spokes in stock, and as a global constant the number of spokes needed to make one wheel. How many wheels can we make, how many rims and spokes will be left over? There are three computed values to pass back to the calling program. We choose in the example below to return as the result of the function the number of wheels it is possible to make, and to store in two address parameters the numbers of remaining rims and spokes.

    A call might be

    We could instead have had a void function, and pass three values back through address parameters.

    Warning 1

    Do not use the "&" sign unless it is really needed. The fact that most parameters are "called by value" is a safety feature, to ensure that a function cannot accidentally change the values of program variables unintentionally.

    Warning 2

    If you have nominated a parameter to be called by address, it does then not make sense to substitute a specific value in a call, as in

    or

    since the object "3" or "i+1" is not something which can be swapped.

    5.11. Jargon reminder

    The parameters as specified at the start of the function definition are referred to as "formal parameters".

    The parameters substituted in any particular call of the function are referred to as "actual parameters".

    5.12. A complete example

    The complete program with function declaration, main program and function definition will look roughly as follows. We use an example with the Halberstam function.

    Note

    In general functions will perform operations on data, and not perform their own input output except

  • (i) if the main purpose of the function is for input/output; or
  • (ii) to print error messages.

    5.13. Examples

    5.13.1. Circle radii and areas

    We show here two functions taking float parameters, and giving float results. The first takes a radius length, and delivers the area of a circle of that radius. The second takes an area, and delivers the radius of a circle with that area.

    Calls of these functions might be

    5.13.2. Summation

    This function sums the series

    for a given value of "x". We keep adding terms until we reach a term whose value is less than 0.001.

    Calls of this function might be

    5.13.3. The OK function

    We require a function to print out a given message as a question, and return TRUE if the user replies "y" and FALSE otherwise. The type of a message (a string of text contained within double quotes) is "char *" (the reasons for this will appear later), and the definition of the function might be:

    In a real situation, the function body might repeat the question until the given answer is "y" or "n". In the above example, we respond with TRUE if the answer is "y" and FALSE otherwise.

    Note that we do not need to say

    We can return the comparison result directly.

    Calls of this function might be:

    Observe the subtlety of the last example. The program first asks

    and, because the "&&" operator is lazy, only if the reply to the first question is "y" asks for confirmation

    If the reply to the first question is "n", the second question is not asked.

    5.14. Mathematical functions

    A number of standard mathematical functions are available. For details try

    or

    5.15. Typographic layout

    Your program layout should now be as typified by the above examples. The function declarations are near the top, all starting at the left hand edge.

    The function definitions follow the program. The first line and the final "}" of each definition start in the first column. The internal statements are indented, the final "}"s are followed by a comment on the same line.

    Copyright Eric Foxley 1996


    Notes converted from troff to HTML by an Eric Foxley shell script, email errors to me!