Previous Section Main Menu Next Section

Chapter 4

Design with Functions

Section IV: Parameters Section I: Decomposition Section II: Functions Section III: Output, Calculation, and Display Functions
Section V: Tracing Section VI: Final Thoughts on Redesigning the Employee Salary Problem

A. Redesigning the Employee Salary Problem
There are many ways to decompose a problem. In the first section of this chapter we decomposed the Employee Salary problem into the following modules:


Notice that the two " Get ....:" modules are very similar and one could easily have seen this as one module:

GetEmployeeInformation

This module could then have been further decomposed into the two modules we originally discovered. Our structure chart would then have looked like:


In some ways this is a better approach since it captures the very common process of developing a high level decomposition, followed by a refined decomposition. Unfortunately, this approach can not be implemented in C++ given what you know. Ask yourself the question, "What should 'GetEmployeeInformation' return to its calling function?" The answer is the hours worked and the rate of pay - two values. As we have seen, however, functions can only return one value using the normal mechanism. You are about to learn more about how function parameters work and a bit of 'magic' that will be further explained in chapter 7. Focus on the new information and just accept the magic for now.

B. How Parameters Work
The variables declared inside the parentheses in a function declaration are referred to as the formal parameters while the parameters on the calling side are called the actual parameters. As an example, let us use the function CalculateSalary. The declaration for this function was written as:

double CalculateSalary(double rate, double hours);

and therefore 'rate' and 'hours' are formal parameters. On the calling side, we wrote:

fullSalary = CalculateSalary(rateOfPay, hoursWorked);

so 'rateOfPay' and 'hoursWorked' are the actual parameters.

The formal parameters we have worked with so far have acted as one way streets or one way communication paths. Information is only passed from the calling function to the called function or from the actual to the formal parameters but not back. In this example a value was assigned to 'rateOfPay' and, likewise, to 'hoursWorked' in 'main'. These variables were then used as actual parameters to pass their values to the formal parameters 'hours' and 'rate'.

When a function has multiple parameters, the first value in the actual parameter list is passed to the first value in the formal parameter list, the second to the second, etc. The names have nothing to do with the way the values are passed. In fact, if we had messed up and written the function call as:

fullSalary = CalculateSalary(hoursWorked, rateOfPay);

the first actual parameter, 'hoursWorked', would have sent its value to the first formal parameter, 'rate'. The variable 'rate' in the function 'CalculateSalary' would have held the hours worked! OOPS!!! Of course, 'hours' would have held the rate of pay - for a double OOPS! The moral of the story: Don't think that the compiler cares a bit about the names you give to your parameters.

You must be careful then with the way you code parameters. In this case, you would have received no error message from the compiler but your program sure would have provided funny answers. Another reason to

TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST

your code.

C. Using Parameters to Return Values
As usual, we got side tracked. The original question concerned the possibility of declaring a function such as 'GetEmployeeInformation' when we know that it needs to return two values, in this case, the rate of pay and the hours worked. As we noted, the actual/formal parameter system we have been working with acts as a one way communication path - from actual to formal. Remember that formal parameters are actually local variables, known only to the function they are declared in. This means that each local variable has its own memory location and any changes to that memory location have no impact on a corresponding variable acting as an actual parameter. To concretize this, suppose we rewrote the function declaration for CalculateSalary so that the formal parameter names matched the actual parameter names in the call in 'main'. The call would be the same as it was:

fullSalary = CalculateSalary(rateOfPay, hoursWorked);

and the declaration would be:

double CalculateSalary(double rateOfPay, double hoursWorked);

If we assume that the user entered the values 6.25 for the 'rateOfPay' and 38 for 'hoursWorked' the following might be a stylized view of memory after the parameter values were passed but before any of the code in the function had executed.


Note that there are two memory locations labeled 'rateOfPay' and two labeled 'hoursWorked' - one set for 'main' and one for 'CalculateSalary'.

Then, suppose that for some, silly reason we include the line:

rateOfPay = 3.3;

inside the function definition for 'CalculateSalary'. When this line was executed, memory would look as follows:


The interesting thing is that the 'local' version of 'rateOfPay' gets this new value BUT the variable with the same name in 'main' retains its old value. In other words, the code we write in the function 'CalculateSalary' has no effect on the variables in 'main'.

What you need to understand is that, given what you have seen so far, there is no way to change a variable contained in one function through an instruction executed in another function - except via the 'return' instruction. And, since the 'return' instruction only allows one value to be returned, we seem to be stuck with regard to the 'GetEmployeeInformation' function.

There is, however, a solution. If we rewrite the function declaration above as:

double CalculateSalary(double& rateOfPay, double hoursWorked);

(note the ampersand after the word 'double' ), the picture of memory changes. Now, and here is the magic that we won't explain for now, there is only one memory location labeled 'rateOfPay' and it is used by both 'main' and 'Calculate Salary'.


Now, if again we include the same instruction

rateOfPay = 3.3;

inside the function definition, the variable in both places (the functions CalculateSalary and 'main') has its value changed. As a matter of fact, as we just noted, there are not two variables - two separate memory locations with the symbolic name 'rateOfPay' - but one.

Further, if we go back to the original function declaration (where the first parameter has the symbolic name 'rate' instead of 'rateOfPay') and add the ampersand:

double CalculateSalary(double& rate, double hours);

there is still only one memory location for the rate of pay. It simply has two symbolic names (like nicknames) for that one memory location.


Therefore, if we include the line:

rate = 3.3

in the function definition, the result is that 'rate' in 'CalculateSalary' and 'rateOfPay' in 'main' both contain the same value, 3.3! In other words, the ampersand forces the first parameter pair (formal and actual) to act as a two-way street. Information can move both ways. (As we said earlier, for now treat this as magic unless you want to jump ahead to chapter 7.

D. Declaring 'GetEmployeeInformation' to Return Two Values
Now we see how we can have 'GetEmployeeInformation' return two pieces of data. Technically, one can return two values by having one value returned via the 'return' instruction and the second returned via a formal parameter declared with an ampersand. Many software engineers, however, don't approve of this. They feel that when you are returning two values of the same kind – as in two pieces of information about an employee – you should declare two formal parameters with the ampersand and declare the function itself with 'void' for the return type. That is what we will do with the function 'GetEmployeeInformation'.

We declare the function with two formal parameters, each with an ampersand following the type name, as in:

void GetEmployeeInformation(double& rate, double& hours);

Since the 'return' mechanism is not used, the function return type is 'void' The ampersands tell the system to treat both formal/actual parameter pairs as two-way streets, allowing information to pass both ways. Actually, since 'GetEmployeeInformation' does not receive any information we don't need to send any information to the function but it is not possible in C++ to set up a one way only heading back from the function. We have a two way street where in one direction garbage is sent. (See the commentary below the following code for further discussion of this 'garbage'.)

To finish up this process, here is the code - first, the segment of the code for 'main' that calls this function and second the code for the definition for this function:

The code for 'GetEmployeeInformation' is amazingly simple - it assumes that two functions exist to actually get the necessary data and hands off to them the dirty work of actually asking the user for the information and perhaps verifying that it is valid. The code does not include a 'return' statement because the return mechanism is not being used to return the information. The ampersands allow the information to be returned.

Note that 'GetEmployeeInformation' does not care how the information is retrieved from the two 'Get....' functions, just that it is retrieved. At the other end, 'main' calls GetEmployeeInformation over and over inside the 'for' loop, not caring how 'GetEmployeeInformation' performs its task.

The first time through the loop the variables 'rateOfPay' and 'hoursWorked' have garbage in them that is passed to 'GetEmployeeInformation'. This means that the variables 'rate' and 'hours' also contain garbage when 'GetEmployeeInformation' is called since they are nicknames for 'rateOfPay' and 'hoursWorked' . This is not a problem, however, because these garbage values are never used. The code says that 'rate' and 'hours' are simply given new values which, in turn, means that 'rateOfPay' and 'hoursWorked' also have new values.

On each subsequent pass through the loop, 'rateOfPay' and 'hoursWorked' have the values for the previous employee and this information is passed to 'GetEmployeeInformation'. Again, however, this is irrelevant because this information is not use by 'GetEmployeeInformation' and the memory locations holding this information immediately have the information for the next employee placed in them.

If this is not clear, consider the following, perhaps slightly stretched, example to clarify the situation. Imagine that you are employed by someone to gather information on students. The person who has hired you wants to gather information that she then uses in some way before throwing it away. The person gives you a box into which you are to put some papers containing the information on the first student. The box has some garbage in it which you throw away and replace with the papers with the information on the first student. You give the box back to the person who hired you, she looks at it, does something with the information, and then passes you back the box for the next student. The box still contains the information from the previous student so you throw it away, gather the information on the next student and again return the box. This continues some number of times until the person who hired you is finished.

A program that uses the function GetEmployeeInformation can be found in the file ch4prg2.cpp. Be sure you see how this example relates to the interplay between 'main' and 'GetEmployeeInformation'.

Top of Section Main Menu Next Section