Functions in C++

Introduction

This lab will provide students with an introduction to the fundamentals of user-defined functions in C++. At the end of this module, students will…

  • Understand the purpose of user-defined functions in C++.
  • Understand the structure of user-defined functions in C++.
  • Be able to create user-defined functions in C++.
  • Be able to apply user-defined functions in C++ within their own programs.

These exercises have two different types of answers. Some questions ask for answers to specific questions; you should collect these answers in a Microsoft Word document. Other exercises ask you to write code. Let’s begin!

Part 1: Programming with Functions in C++

We’ve said before that C++ code is broken down into small code segments called functions. These functions, when combined, form a program. Until now we have only dealt with one function called main; however, we can define our own functions to make them easier to read and to maintain. In addition, C++ provides us with a number of useful functions to perform common tasks, like finding square roots of numbers and converting between different numerical representations.

In this set of exercises, we will learn how to use some of the built-in C++ functions to simplify our programs, as well as learn how to create our own. In the future, our programs will grow to include a larger number of functions.

Function Basics

Functions are defined as a unit of computational abstraction. In programming, as in life, very often we want to ignore the small details and concentrate on the big picture. Functions allow us to do just that, by allowing us to use previously written code to perform a task without worrying about how the task is performed. For instance, in the following two lines of code:

   sum = pow(side1, 2) + pow(side2, 2);
   side3 = sqrt(sum); 

This code uses two functions that C++ provides in the <cmath> standard library. The first line uses the pow function to compute the result of raising a number to a power (in this case, the value of side1 raised to the second power). We don’t know how the function works, all we know is that it will do a certain task and give us a result we desire. Likewise, the second line uses sqrt to compute the square root of a given number (in this case, the value of sum).

When calling a function in C++, the function name is written first, followed by the inputs in parentheses. For example, suppose the variable sum contained the value 9. The function call or invocation sqrt(sum) would return the value 3. Similarly, the call sqrt(25) would return the value 5, and pow(3, 2) would return the value 32 = 9.

A function call can appear anywhere in a C++ expression. When an expression containing a function call is evaluated, the return value for that call is substituted into the expression. For example, in the assignment value = sqrt(4);, the call on the right-hand side would evaluate to 2, and so the variable value would be assigned the value 2.

In order to use a function effectively, we need to know the type of data that is returned by the function. We can only use C++ functions in expressions where data of the same type as the function’s return type is needed. For instance, the following code would not make sense:

     char my_char;
     my_char = sqrt(30);

Because sqrt returns a real number, it does not make sense to try and place that result into a variable of type character. Watch and listen to the What is a C++ Function? podcast before proceeding:

whats_function

Part 2: User-Defined Functions

Until now we have only used functions that were either (a) required by the language, or (b) provided by the C++ Standard Library of functions. The standard library functions help to simplify the programmer’s task in two major ways. First, functions help to minimize the amount of detail of which the programmer must keep track. Since the sqrt function is available, the programmer does not need to remember the sequence of steps involved in computing the square root. He or she only needs to remember how to call the sqrt function. Second, functions help to minimize the size and complexity of code. Once the sqrt function has been defined, a single call to this function will suffice to compute a square root. Since the number of steps required to otherwise compute the square root would be considerable, this can represent a significant savings in the complexity of code.

The C++ Standard Library is designed to provide the programmer with a set of common tools which can be used without worrying about the nitty-gritty details of how they do their job. These predefined functions represent a collection of useful, general purpose abstractions. In addition to these, you are able to define new abstractions by defining your own functions.

Any computation that you find especially useful can be encapsulated as a function. Once defined and loaded into a C++ file, your function can be called just like any predefined function. Therefore, your function may be viewed as extending the capabilities of the C++ language itself.

As an example, consider the formula for converting a temperature in degrees Fahrenheit (f) to degrees Celsius (c):

   c = (5.0 / 9.0) * (f - 32) 

We can use this formula to create a C++ program. If converting temperatures was a task that you were to perform relatively often in other programs, you would spend a lot of time remembering and retyping this formula. Alternatively, you could encapsulate this computation in a function and save it in a C++ file. Any time you wanted to convert temperatures, you could then add this C++ file to your project and call the function. You wouldn’t need to remember the formula anymore, just the name of the function and how to call it.

The following is the definition of a C++ function that performs the temperature conversion. A description of the key parts of this definition is given below:

///////////////////////////////////////////////////////////////////////////
//  
//   FUNCTION NAME: fahrToCelsius
//      PARAMETERS: double fahr   -- The degrees in Fahrenheit
//     RETURN TYPE: double
//         PURPOSE: Converts a given degree reading from F to C.
//
///////////////////////////////////////////////////////////////////////////

double fahrToCelsius(double fahr)
{
   // variable declarations and initialization
   double celsius = 0.0;

   // convert the value from F to C...
   celsius = (5.0 / 9.0) * (fahr - 32);

   // return the converted value...
   return celsius;
}
  • The first eight lines are a function header comment, describing the important aspects of the function (name, inputs, output, and purpose). You should always have comments in your functions to improve readability.
  • The next line is the function header. The header specifies that you are defining a function named fahrToCelsius with one input. The input value is represented by a variable inside of the parentheses, also known as a parameter This variable name (fahr) is preceded by the data type of that variable (in this case, double). When the function is called, the input specified in the function call is assigned to the parameter for use in the function’s computation. For example, for the call fahrToCelsius(212), the parameter fahr would be assigned the value 212. Thus, the value 212 would be used in the function code as the value for fahr, resulting in the function returning the value 100.
  • The actual code that carries out the computation of the function is enclosed in curly-braces and is called the function body. In this example, there is only three C++ code statements, but functions can contain any number of statements. A return statement is a special C++ statement that specifies the value that should be returned by the function. It consists of the keyword return followed by an expression. When a return statement is executed, the expression is evaluated and the resulting value is returned as the value of the function call.

In order to make a user-defined function accessible in a C++ program, it must be included in the C++ program. This can be accomplished in two ways. When the function is specific to that program only, its definition can be inserted directly into the .cpp file in the project; for instance, we could have a single file in our fahrToCelsius program with two functions: main and fahrToCelsius. Alternatively, general-purpose function definitions can be entered into a separate library file and loaded into your program. In the future, we will see how to do this.

Exercise 1: Create a new C++ code file called ConvertTemp.cpp and add the following code to the file:

///////////////////////////////////////////////////////////////////////////
//
//       Name:  convertTemp.cpp
//     Author:  Jeffrey A. Stone
//    Purpose:  Program which converts a Fahrenheit reading to 
//              Celsius. 
//
///////////////////////////////////////////////////////////////////////////

#include <iostream>

using namespace std;

///////////////////////////////////////////////////////////////////////////
//  
//   FUNCTION NAME: fahrToCelsius
//      PARAMETERS: double fahr   -- The degrees in Fahrenheit
//     RETURN TYPE: double
//         PURPOSE: Converts a given degree reading from F to C.
//
///////////////////////////////////////////////////////////////////////////

double fahrToCelsius(double fahr)
{
   // variable declarations and initialization
   double celsius = 0.0;

   // convert the value from F to C...
   celsius = (5.0 / 9.0) * (fahr - 32);

   // return the converted value...
   return celsius;
}


///////////////////////////////////////////////////////////////////////////
//  
//   FUNCTION NAME: main
//      PARAMETERS: None
//     RETURN TYPE: int
//         PURPOSE: Entry point for the application.
//
///////////////////////////////////////////////////////////////////////////

int main()
{
   // variable declaration and initialization
   double fahr_temp = 0.0;

   // variable declaration and initialization
   double cel_temp = 0.0;

   // prompt the user...
   cout << "Please enter the fahrenheit temperature: ";
   // input the value from the user...
   cin  >> fahr_temp;

   // convert...
   cel_temp = fahrToCelsius(fahr_temp);
 	 
   // output the temperature, in both units...
   cout << "You entered " << fahr_temp << " F degrees." << endl
        << "That is " << cel_temp << " C degrees." << endl;

   // return success to the Operating System...
   return 0;
} 

Compile and run the program, and verify that it works as it should. Next, watch and listen to the Walking Through a Function Call in C++ podcast before proceeding. This podcast will present a dynamic view of how the above program executes:

Exercise 2: Add a new function to convertTemp.cpp called celsiusToFahr. Modify the code in main to input two values: the degree reading, along with an F or a C, with the two values separated by a space.

  • If the user inputs an F, the degree reading is in Fahrenheit; your program should convert the degrees to Celsius and output the result to the screen.
  • If the user inputs a C, the degree reading is in Celsius; your program should convert the degrees to Fahrenheit and output the result to the screen.
  • Don’t forget to properly comment your code!

Hint: You case use a predefined function called toupper to convert a character from lowercase to uppercase. This may prove useful for your conditional tests. For example, simplechar = toupper(simplechar); takes the value of simplechar and (if it is an alphabetic character) returns the uppercase version of the letter. Simply add #include <cctype> right below #include <iostream> to gain access to the function.

Local Variables

Notice from Exercise 1 that the function fahrToCelsius contains a single variable declaration, namely

double celsius = 0.0;

The first line in this function is a variable declaration specifying that the new variable celsius is to be used in the function. Declaring variables in this way signifies that they are local to the function, meaning they will only exist inside of the function. In this sense, local variables are similar to parameters. Declaring a local variable tells the C++ compiler that you are going to use it as temporary storage while computing the function value, and that the storage should go away – i.e. be returned to the pool of available storage – when execution of the function is complete.

In all functions, including main, variables have a defined scope, the area(s) and time for which they are allowed to be used. In the case of C++ functions, this scope is limited to the code statements within the beginning and ending brackets ({ }). The lifetime of these variables (i.e. the time from which memory is allocated by the system to the time in which the memory is given back) is the time for which the function is executing.

Global variables are variables declared outside of any function. Global variables are thus accessible by any function within the code file. While the C++ language permits this functionality, it is generally considered poor programming practice to declare variables with global scope. Do not declare global variables in any program you create in this course.

Part 2: Function Calling Sequence

Whenever a function is called, a specific sequence of events takes place. If you understand this sequence, it should be possible to trace the execution of any function and ascertain its behavior. For example, consider the following program that computes the sum of a range of integers.

///////////////////////////////////////////////////////////////////////////
//
//       Name:  sumNumbers.cpp
//     Author:  Jeffrey A. Stone
//    Purpose:  Sums the numbers in a user-defined sequence.
//
///////////////////////////////////////////////////////////////////////////
#include <iostream>

using namespace std;

///////////////////////////////////////////////////////////////////////////
//  
//   FUNCTION NAME: sumNumbers
//      PARAMETERS: long min     -- The lower bound value.
//                  long max     -- The upper bound value.
//     RETURN TYPE: long
//         PURPOSE: Sums the numbers from low to high.
//
///////////////////////////////////////////////////////////////////////////

long sumNumbers(long min, long max)
{
   long counter = 0;   // keeps a “count” of the number of loops
   long sum = 0;       // keeps a running sum of values

   // set the counter to the min value...
   counter = min;

   while (counter <= max)
   {
	   // Add to our running total...
	   sum = sum + counter;

	   // Add to lCounter...
	   counter = counter + 1;
   }

   // return the sum of the numbers in the sequence...
   return sum;
}

///////////////////////////////////////////////////////////////////////////
//  
//   FUNCTION NAME: main
//      PARAMETERS: None
//     RETURN TYPE: int
//         PURPOSE: Entry point for the application.
//
///////////////////////////////////////////////////////////////////////////

int main()
{
   long low = 0;    // stores an input value
   long high = 0;   // stores an input value

   cout << "Enter two integers separated by a space, "   // prompt
        << "smallest integer first : ";                   
   cin  >> low >> high;                                   // input
 	
   cout << "\nThe sum from " << low << " to " << high;    // output
   cout << " is " << sumNumbers(low, high) << "\n";       // output
   
   return 0;   // return "success" to the operating system…
}

When the sumNumbers function is called in the main function, the following events occur:

  1. Execution shifts to the sumNumbers function.
  2. The inputs to the function are evaluated first, with the parameters min and max assuming the values contained within low and high, respectively.
  3. Within the function, new memory cells are allocated for the parameters min and max. These memory cells are assigned the values of the corresponding arguments from the call: the values contained within the variables low and high, respectively. Throughout the function, any references to these parameters will be associated with these memory cells.
  4. Memory cells are next allocated (created) for each local variable: counter and sum. Similarly, any references within the function to these local variable names will be associated with these memory cells. They are initialized with a zero value.
  5. The statements in the function are executed in order, assigning sum the value of the sum of the integers from min to max.
  6. When the return statement is reached, the expression in the statement is evaluated, yielding the value contained within sum.
  7. After computing the return value, the execution of the function is over. Since the local variables exist only inside the function itself, the memory cells associated with the variables are de-allocated, i.e., made available for other uses.
  8. Similarly, the memory cells associated with parameters are de-allocated.
  9. Once the memory cells have been freed, the value in sum can be returned as the value of the function call. In this case, the value returned by the function replaces the function call in the assignment, and so that value is output to the screen.

In general, the calling sequence for functions is as follows:

  1. The arguments in the function call are evaluated.
  2. Execution shifts to the function being called.
  3. Memory cells are allocated (created) for each parameter in the function, and the values of the corresponding arguments are assigned to these memory cells.
  4. Memory cells are allocated (created) for each local variable.
  5. The statements in the body of the function are executed in order.
  6. When a return statement is encountered, the expression is evaluated.
  7. Memory cells associated with the local variables are de-allocated (destroyed).
  8. Memory cells associated with the parameters are de-allocated (destroyed).
  9. Upon return, the value of the expression in the return statement replaces the function call in whatever expression it appears in.

Watch and listen to the Function Calling Sequence podcast before proceeding:

calling_sequence
Exercise 3: Create a new C++ file called incomeTax.cpp. Your main function should prompt the user for his/her gross income and itemized deduction (both double values). Construct a function called incomeTax which computes the tax due using the input values (gross income and itemized deduction) as follows:

  • The taxpayer’s taxable income is determined by subtracting the taxpayer’s deduction from the taxpayer’s gross income.
  • Total tax is determined by multiplying the taxable income by the tax rate (0.17).

Your main function should call your incomeTax function to compute the total tax due, giving it appropriate arguments. Your incomeTax function should return the total tax due back to your main function. Finally, your main function should output the tax due to the screen with a descriptive message.

Please watch and listen to the Techniques for User-Defined Functions in C++ podcast before proceeding. This podcast will present a description of how to create input, processing/computation, and output functions. These tips will be used heavily in this course.

techniques

Part 4: Vocabulary Words

You will be assessed on your knowledge of the following terms and concepts as well as the C++ concepts discussed in this document. As a result, you are encouraged to review these terms in the preceding document.

Argument – a value that is provided in a function call to “fill in” a parameter. Arguments provide functions with the data necessary to complete its task.

Comment – a statement, sentence, or phrase which describes a piece of code. Comments are ignored by the compiler and exist only to document the C++ code with a more natural language (e.g. English). In C++, single-line comments begin with //. Multi-line comments are bounded with /* and */.

C++ Standard Library – a set of common tools which can be used in any program. These predefined functions and classes represent a collection of useful, general purpose abstractions.

Function – a specific unit of code in C++ containing a header and a body. Functions can be “called” by other functions to perform tasks, and can “return” a value back to the “caller”.

Function Body – The actual code that carries out the computation of the function; that part of the function which is enclosed in curly-braces.

Function Call – the act of invoking a function. A function call is made by using the function name and providing the function with any necessary arguments.

Function Header – the first line of a function, containing the function name, the parameter list, and the type of data returned by the function.

Local Variable – a variable declared within a set of { }, such as a function body. Local variables can only be used within that set of { }. Declaring a local variable tells the C++ compiler that you are going to use it as temporary storage while computing the function value, and that the storage should go away – i.e. be returned to the pool of available storage – when execution of the function is complete.

Parameter – a value that a function expects to be “given” when the function is called. A function can have 0 or more parameters. These are placeholders that must be “filled in” by arguments when the function is called. As a result, parameters can take on different values each time the function is called. Parameters act as local variables within the function body.

Parameter List – a set of parameters, enclosed in parentheses within the function header. Parameter lists follow the function name in a function header. Each parameter is declared using a data type and a name, much like a variable declaration. The parameter declarations in the list are separated by commas.

Return Statement – a special C++ statement that specifies the value that should be returned by the function; begins with the keyword “return”.

Scope – the “visibility” or “lifetime” of a variable. All variables have a defined area and time for which they are allowed to be used. In the case of C++ functions, this scope is limited to the code statements within the beginning and ending brackets ({ }). The lifetime of these variables (i.e. the time from which memory is allocated by the system to the time in which the memory is given back) is the time for which the function is executing.