7.2 Arrays in Functions

You can use both array indexed variables and entire arrays as arguments to functions. We first discuss array indexed variables as arguments to functions.

Indexed Variables as Function Arguments

An indexed variable can be an argument to a function in exactly the same way that any variable can be an argument. For example, suppose a program contains the following declarations:

int i, n, a[10];

If myFunction takes one argument of type int, then the following is legal:

myFunction(n);

Since an indexed variable of the array a is also a variable of type int, just like n, the following is equally legal:

myFunction(a[3]);

There is one subtlety that does apply to indexed variables used as arguments. For example, consider the following function call:

myFunction(a[i]);

If the value of i is 3, then the argument is a[3]. On the other hand, if the value of i is 0, then this call is equivalent to the following:

myFunction(a[0]);

The indexed expression is evaluated in order to determine exactly which indexed variable is given as the argument.

Display 7.3 contains an example of indexed variables used as function arguments. The program shown gives five additional vacation days to each of three employees in a small business. The program is extremely simple, but it does illustrate how indexed variables are used as arguments to functions. Notice the function adjustDays. This function has a formal parameter called oldDays that is of type int. In the main body of the program, this function is called with the argument vacation[number] for various values of number. Notice that there was nothing special about the formal parameter oldDays. It is just an ordinary formal parameter of type int, which is the base type of the array vacation. In Display 7.3 the indexed variables are call-by-value arguments. The same remarks apply to call-by-reference arguments. An indexed variable can be a call-by-value argument or a call-by-reference argument.

Display 7.3 Indexed Variable as an Argument

 1    //Illustrates the use of an indexed variable as an argument.
 2    //Adds 5 to each employee's allowed number of vacation days.
 3    #include <iostream>
 4    const int NUMBER_OF_EMPLOYEES = 3;

 5    int adjustDays(int oldDays);
 6    //Returns oldDays plus 5.

 7    int main( )
 8    {
 9        using namespace std;
10        int vacation[NUMBER_OF_EMPLOYEES], number;
11        cout << "Enter allowed vacation days for employees 1"
12             << " through " << NUMBER_OF_EMPLOYEES << ":\n";
13        for (number = 1; number <= NUMBER_OF_EMPLOYEES; number++)
14            cin >> vacation[number − 1];
15        for (number = 0; number < NUMBER_OF_EMPLOYEES; number++)
16            vacation[number] = adjustDays(vacation[number]);
17        cout << "The revised number of vacation days are:\n";
18        for (number = 1; number <= NUMBER_OF_EMPLOYEES; number++)
19            cout << "Employee number " << number
20                 << " vacation days = " << vacation[number-1] << endl;
21        return 0;
22    }
23    int adjustDays(int oldDays)
24    {
25          return (oldDays + 5);
26    }

Sample Dialogue

Enter allowed vacation days for employees 1 through 3:
10 20 5
The revised number of vacation days are:
Employee number 1 vacation days = 15
Employee number 2 vacation days = 25
Employee number 3 vacation days = 10

Self-Test Exercises

  1. Consider the following function definition:

    void tripler(int& n)
    {
        n = 3*n;
    }

    Which of the following are acceptable function calls?

    int a[3] = {4, 5, 6}, number = 2;
    tripler(number);
    tripler(a[2]);
    tripler(a[3]);
    tripler(a[number]);
    tripler(a);
  2. What (if anything) is wrong with the following code? The definition of tripler is given in Self-Test Exercise 11.

    int b[5] = {1, 2, 3, 4, 5};
    for (int i = 1; i <= 5; i++)
        tripler(b[i]);

Entire Arrays as Function Arguments

A function can have a formal parameter for an entire array so that when the function is called, the argument that is plugged in for this formal parameter is an entire array. However, a formal parameter for an entire array is neither a call-by-value parameter nor a call-by-reference parameter; it is a new kind of formal parameter referred to as an array parameter. Let’s start with an example.

The function defined in Display 7.4 has one array parameter, a, which will be replaced by an entire array when the function is called. It also has one ordinary call-by-value parameter (size) that is assumed to be an integer value equal to the size of the array. This function fills its array argument (that is, fills all the array’s indexed variables) with values typed in from the keyboard, and then the function outputs a message to the screen telling the index of the last array index used.

The formal parameter int a[ ] is an array parameter. The square brackets, with no index expression inside, are what C++ uses to indicate an array parameter. An array parameter is not quite a call-by-reference parameter, but for most practical purposes it behaves very much like a call-by-reference parameter. Let’s go through this example in detail to see how an array argument works in this case. (An array argument is, of course, an array that is plugged in for an array parameter, such as a[ ].)

When the function fillUp is called it must have two arguments: The first gives an array of integers, and the second should give the declared size of the array. For example, the following is an acceptable function call:

int score[5], numberOfScores = 5;
fillUp(score, numberOfScores);

This call to fillUp will fill the array score with five integers typed in at the keyboard. Notice that the formal parameter a[ ] (which is used in the function declaration and the heading of the function definition) is given with square brackets, but no index expression. (You may insert a number inside the square brackets for an array parameter, but the compiler will simply ignore the number, so we do not use such numbers in this book.) On the other hand, the argument given in the function call (score in this example) is given without any square brackets or any index expression. What happens to the array argument score in this function call? Very loosely speaking, the argument score is plugged in for the formal array parameter a in the body of the function, and then the function body is executed. Thus, the function call

Display 7.4 Function with an Array Parameter

Function Declaration

 1    void fillUp(int a[], int size);
 2    //Precondition: size is the declared size of the array a.
 3    //The user will type in size integers.
 4    //Postcondition: The array a is filled with size integers
 5    //from the keyboard.

Function Definition

 1    //Uses iostream:
 2    void fillUp(int a[], int size)
 3    {
 4        using namespace std;
 5        cout << "Enter " << size << " numbers:\n";
 6        for (int i = 0; i < size; i++)
 7            cin >> a[i];
 8        size−−;
 9        cout << "The last array index used is " << size << endl;
10    }
fillUp(score, numberOfScores);

is equivalent to the following code:

An illustration some lines of code with the line “size = 5;” annotated as “5 is the value of numberOfScores.”

The formal parameter a is a different kind of parameter from the ones we have seen before now. The formal parameter a is merely a placeholder for the argument score. When the function fillUp is called with score as the array argument, the computer behaves as if a were replaced with the corresponding argument score. When an array is used as an argument in a function call, any action that is performed on the array parameter is performed on the array argument, so the values of the indexed variables of the array argument can be changed by the function. If the formal parameter in the function body is changed (for example, with a cin statement), then the array argument will be changed.

So far it looks like an array parameter is simply a call-by-reference parameter for an array. That is close to being true, but an array parameter is slightly different from a call-by-reference parameter. To help explain the difference, let’s review some details about arrays.

Recall that an array is stored as a contiguous chunk of memory. For example, consider the following declaration for the array score:

int score[5];

When you declare this array, the computer reserves enough memory to hold five variables of type int, which are stored one after the other in the computer’s memory. The computer does not remember the addresses of each of these five indexed variables; it remembers only the address of indexed variable score[0]. For example, when your program needs score[3], the computer calculates the address of score[3] from the address of score[0]. The computer knows that score[3] is located three int variables past score[0]. Thus, to obtain the address of score[3], the computer takes the address of score[0] and adds a number that represents the amount of memory used by three int variables; the result is the address of score[3].

Viewed this way, an array has three parts: the address (location in memory) of the first indexed variable, the base type of the array (which determines how much memory each indexed variable uses), and the size of the array (that is, the number of indexed variables). When an array is used as an array argument to a function, only the first of these three parts is given to the function. When an array argument is plugged in for its corresponding formal parameter, all that is plugged in is the address of the array’s first indexed variable. The base type of the array argument must match the base type of the formal parameter, so the function also knows the base type of the array. However, the array argument does not tell the function the size of the array. When the code in the function body is executed, the computer knows where the array starts in memory and how much memory each indexed variable uses, but (unless you make special provisions) it does not know how many indexed variables the array has. That is why it is critical that you always have another int argument telling the function the size of the array. That is also why an array parameter is not the same as a call-by-reference parameter. You can think of an array parameter as a weak form of call-by-reference parameter in which everything about the array is told to the function except for the size of the array.2

These array parameters may seem a little strange, but they have at least one very nice property as a direct result of their seemingly strange definition. This advantage is best illustrated by again looking at our example of the function fillUp given in Display 7.4. That same function can be used to fill an array of any size, as long as the base type of the array is int. For example, suppose you have the following array declarations:

int score[5], time[10];

The first of the following calls to fillUp fills the array score with five values and the second fills the array time with ten values:

fillUp(score, 5);
fillUp(time, 10);

You can use the same function for array arguments of different sizes because the size is a separate argument.

The const Parameter Modifier

When you use an array argument in a function call, the function can change the values stored in the array. This is usually fine. However, in a complicated function definition, you might write code that inadvertently changes one or more of the values stored in an array, even though the array should not be changed at all. As a precaution, you can tell the compiler that you do not intend to change the array argument, and the computer will then check to make sure your code does not inadvertently change any of the values in the array. To tell the compiler that an array argument should not be changed by your function, you insert the modifier const before the array parameter for that argument position. An array parameter that is modified with a const is called a constant array parameter.

For example, the following function outputs the values in an array but does not change the values in the array:

void showTheWorld(int a[ ], int sizeOfA)
//Precondition: sizeOfA is the declared size of the array a.
//All indexed variables of a have been given values.
//Postcondition: The values in a have been written
//to the screen.
{
    cout << "The array contains the following values:\n";
    for (int i = 0; i < sizeOfA; i++)
        cout << a[i] << " ";
    cout << endl;
}

This function will work fine. However, as an added safety measure you can add the modifier const to the function heading as follows:

void showTheWorld(const int a[ ], int sizeOfA)

With the addition of this modifier const, the computer will issue an error message if your function definition contains a mistake that changes any of the values in the array argument. For example, the following is a version of the function showTheWorld that contains a mistake that inadvertently changes the value of the array argument. Fortunately, this version of the function definition includes the modifier const, so that an error message will tell us that the array a is changed. This error message will help to explain the mistake:

void showTheWorld(const int a[ ], int sizeOfA)
//Precondition: sizeOfA is the declared size of the array a.
//All indexed variables of a have been given values.
//Postcondition: The values in a have been written
//to the screen.
An illustration shows a code, with “for(int i = 0; i < sizeOfA; a[i]++)” annotated as “Mistake, but the compiler will not catch it unless you use the const modifier.”

If we had not used the const modifier in this function definition and if we made the mistake shown, the function would compile and run with no error messages. However, the code would contain an infinite loop that continually increments a[0] and writes its new value to the screen.

The problem with this incorrect version of showTheWorld is that the wrong item is incremented in the for loop. The indexed variable a[i] is incremented, but it should be the index i that is incremented. In this incorrect version, the index i starts with the value 0 and that value is never changed. But a[i], which is the same as a[0], is incremented. When the indexed variable a[i] is incremented, that changes a value in the array, and since we included the modifier const, the computer will issue a warning message. That error message should serve as a clue to what is wrong.

You normally have a function declaration in your program in addition to the function definition. When you use the const modifier in a function definition, you must also use it in the function declaration so that the function heading and the function declaration are consistent.

The modifier const can be used with any kind of parameter, but it is normally used only with array parameters and call-by-reference parameters for classes, which are discussed in Chapter 11.

Functions That Return an Array

A function may not return an array in the same way that it returns a value of type int or double. There is a way to obtain something more or less equivalent to a function that returns an array. The thing to do is to return a pointer to the array. However, we have not yet covered pointers. We will discuss returning a pointer to an array when we discuss the interaction of arrays and pointers in Chapter 9. Until then, you have no way to write a function that returns an array.

Case Study Production Graph

In this case study we use arrays in the top-down design of a program. We use both indexed variables and entire arrays as arguments to the functions for subtasks.

Problem Definition

The Apex Plastic Spoon Manufacturing Company has commissioned us to write a program that will display a bar graph showing the productivity of each of its four manufacturing plants for any given week. Plants keep separate production figures for each department, such as the teaspoon department, soup spoon department, plain cocktail spoon department, colored cocktail spoon department, and so forth. Moreover, each plant has a different number of departments. For example, only one plant manufactures colored cocktail spoons. The input is entered plant-by-plant and consists of a list of numbers giving the production for each department in that plant. The output will consist of a bar graph in the following form:

Plant #1 **********
Plant #2 *************
Plant #3 *******************
Plant #4 *****

Each asterisk represents 1000 units of output.

We decide to read in the input separately for each department in a plant. Since departments cannot produce a negative number of spoons, we know that the production figure for each department will be nonnegative. Hence, we can use a negative number as a sentinel value to mark the end of the production numbers for each plant.

Since output is in units of 1000, it must be scaled by dividing it by 1000. This presents a problem since the computer must display a whole number of asterisks. It cannot display 1.6 asterisks for 1600 units. We will thus round to the nearest 1000th. Thus, 1600 will be the same as 2000 and will produce two asterisks. A precise statement of the program’s input and output is as follows.

Input

There are four manufacturing plants numbered 1 through 4. The following input is given for each of the four plants: a list of numbers giving the production for each department in that plant. The list is terminated with a negative number that serves as a sentinel value.

Output

A bar graph showing the total production for each plant. Each asterisk in the bar graph equals 1000 units. The production of each plant is rounded to the nearest 1000 units.

Analysis of the Problem

We will use an array called production, which will hold the total production for each of the four plants. In C++, array indexes always start with 0. But since the plants are numbered 1 through 4, rather than 0 through 3, we will not use the plant number as the array index. Instead, we will place the total production for plant number n in the indexed variable production[n1]. The total output for plant number 1 will be held in production[0], the figures for plant 2 will be held in production[1], and so forth.

Since the output is in thousands of units, the program will scale the values of the array elements. If the total output for plant number 3 is 4040 units, then the value of production[2] will initially be set to 4040. This value of 4040 will then be scaled to 4 so that the value of production[2] is changed to 4, and four asterisks will be output in the graph to represent the output for plant number 3.

The task for our program can be divided into the following subtasks:

  • inputData: Read the input data for each plant and set the value of the indexed variable production[plantNumber − 1] equal to the total production for that plant, where plantNumber is the number of the plant.

  • scale: For each plantNumber, change the value of the indexed variable production[plantNumber − 1] to the correct number of asterisks.

  • graph: Output the bar graph.

The entire array production will be an argument for the functions that carry out these subtasks. As is usual with an array parameter, this means we must have an additional formal parameter for the size of the array, which in this case is the same as the number of plants. We will use a defined constant for the number of plants, and this constant will serve as the size of the array production. The main part of our program, together with the function declarations for the functions that perform the subtasks and the defined constant for the number of plants, is shown in Display 7.5. Notice that, since there is no reason to change the array parameter to the function graph, we have made that array parameter a constant parameter by adding the const parameter modifier. The material in Display 7.5 is the outline for our program, and if it is in a separate file, that file can be compiled so that we can check for any syntax errors in this outline before we go on to define the functions corresponding to the function declarations shown.

Having compiled the file shown in Display 7.5, we are ready to design the implementation of the functions for the three subtasks. For each of these three functions, we will design an algorithm, write the code for the function, and test the function before we go on to design the next function.

Algorithm Design for inputData

The function declaration and descriptive comment for the function inputData is shown in Display 7.5. As indicated in the body of the main part of our program (also shown in Display 7.5), when inputData is called, the formal array parameter a will be replaced with the array production, and since the last plant number is the same as the number of plants, the formal parameter lastPlantNumber will be replaced by NUMBER_OF_PLANTS. The algorithm for inputData is straightforward:

  • For plantNumber equal to each of 1, 2, through lastPlantNumber do the following:

  • Read in all the data for plant whose number is plantNumber.

  • Sum the numbers.

  • Set production[plantNumber1] equal to that total.

Coding for inputData

The algorithm for the function inputData translates to the following code:

//Uses iostream:
void inputData(int a[ ], int lastPlantNumber)
{
        using namespace std;
        for (int plantNumber = 1;
            plantNumber <= lastPlantNumber; plantNumber++)
        {
        cout << endl
             << "Enter production data for plant number "
             << plantNumber << endl;
        getTotal(a[plantNumber − 1]);
        }
}

The code is routine since all the work is done by the function getTotal, which we still need to design. But before we move on to discuss the function getTotal, let’s observe a few things about the function inputData. Notice that we store the figures for plant number plantNumber in the indexed variable with index plantNumber1; this is because arrays always start with index 0, while the plant numbers start with 1. Also, notice that we use an indexed variable for the argument to the function getTotal. The function getTotal really does all the work for the function inputData.

The function getTotal does all the input work for one plant. It reads the production figures for that plant, sums the figures, and stores the total in the indexed variable for that plant. But getTotal does not need to know that its argument is an indexed variable. To a function such as getTotal, an indexed variable is just like any other variable of type int. Thus, getTotal will have an ordinary call-by-reference parameter of type int. That means that getTotal is just an ordinary input function like others that we have seen before we discussed arrays. The function getTotal reads in a list of numbers ended with a sentinel value, sums the numbers as it reads them in, and sets the value of its argument, which is a variable of type int, equal to this sum. There is nothing new to us in the function getTotal. Display 7.6 shows the function definitions for both getTotal and inputData. The functions are embedded in a simple test program.

Display 7.5 Outline of the Graph Program

 1    //Reads data and displays a bar graph showing productivity for each plant.
 2    #include <iostream>
 3    const int NUMBER_OF_PLANTS = 4;
 4     
 5    void inputData(int a[], int lastPlantNumber);
 6    //Precondition: lastPlantNumber is the declared size of the array a.
 7    //Postcondition: For plantNumber = 1 through lastPlantNumber:
 8    //a[plantNumber − 1] equals the total production for plant number plantNumber.
 9     
10    void scale(int a[], int size);
11    //Precondition: a[0] through a[size − 1] each has a nonnegative value.
12    //Postcondition: a[i] has been changed to the number of 1000s (rounded to
13    //an integer) that were originally in a[i], for all i such that 0 <= i <= size − 1.
14    
15    void graph(const int asteriskCount[], int lastPlantNumber);
16    //Precondition: asteriskCount[0] through asteriskCount[lastPlantNumber − 1] 17    //have nonnegative values.
18    //Postcondition: A bar graph has been displayed saying that plant
19    //number N has produced asteriskCount[N − 1] 1000s of units, for each N such that
20    //1 <= N <= lastPlantNumber
21     
22    int main( )
23    {
24        using namespace std;
25        int production[NUMBER_OF_PLANTS];
26     
27        cout << "This program displays a graph showing\n"
28             << "production for each plant in the company.\n";
29     
30        inputData(production, NUMBER_OF_PLANTS);
31        scale(production, NUMBER_OF_PLANTS);
32        graph(production, NUMBER_OF_PLANTS);
33     
34        return 0;
35    }
36    

Testing inputData

Every function should be tested in a program in which it is the only untested function. The function inputData includes a call to the function getTotal. Therefore, we should test getTotal in a driver program of its own. Once getTotal has been completely tested, we can use it in a program, like the one in Display 7.6, to test the function inputData.

When testing the function inputData, we should include tests with all possible kinds of production figures for a plant. We should include a plant that has no production figures (as we did for plant 4 in Display 7.6); we should include a test for a plant with only one production figure (as we did for plant 3 in Display 7.6); and we should include a test for a plant with more than one production figure (as we did for plants 1 and 2 in Display 7.6). We should test for both nonzero and zero production figures, which is why we included a 0 in the input list for plant 2 in Display 7.6.

Algorithm Design for scale

The function scale changes the value of each indexed variable in the array production so that it shows the number of asterisks to print out. Since there should be one asterisk for every 1000 units of production, the value of each indexed variable must be divided by 1000.0. Then to get a whole number of asterisks, this number is rounded to the nearest integer. This method can be used to scale the values in any array a of any size, so the function declaration for scale, shown in Display 7.5 and repeated here, is stated in terms of an arbitrary array a of some arbitrary size:

void scale(int a[ ], int size);
//Precondition: a[0] through a[size − 1] each has a
//nonnegative value.
//Postcondition: a[i] has been changed to the number of 1000s
//(rounded to an integer) that were originally in a[i], for
//all i such that 0 <= i <= size − 1.

When the function scale is called, the array parameter a will be replaced by the array production, and the formal parameter size will be replaced by NUMBER_OF_PLANTS so that the function call looks like the following:

scale(production, NUMBER_OF_PLANTS);

The algorithm for the function scale is as follows:

for (int index = 0; index < size; index++)

Divide the value of a[index] by 1000 and round the result to the nearest whole number; the result is the new value of a[index].

Coding for scale

The algorithm for scale translates into the C++ code given next, where round is a function we still need to define. The function round takes one argument of type double and returns a type int value that is the integer nearest to its argument; that is, the function round will round its argument to the nearest whole number.

Display 7.6 Test of Function inputData

 1    //Tests the function inputData.
 2    #include <iostream>
 3    const int NUMBER_OF_PLANTS = 4;
 4     
 5    void inputData(int a[], int lastPlantNumber);
 6    //Precondition: lastPlantNumber is the declared size of the array a.
 7    //Postcondition: For plantNumber = 1 through lastPlantNumber:
 8    //a[plantNumber − 1] equals the total production for plant number plantNumber.
 9     
10    void getTotal(int& sum);
11    //Reads nonnegative integers from the keyboard and
12    //places their total in sum.
13     
14    int main( )
15    {
16        using namespace std;
17        int production[NUMBER_OF_PLANTS];
18        char ans;
19     
20        do
21        {
22            inputData(production, NUMBER_OF_PLANTS);
23            cout << endl
24                 << "Total production for each"
25                 << " of plants 1 through 4:\n";
26            for (int number = 1; number <= NUMBER_OF_PLANTS; number++)
27                cout << production[number − 1] << " ";
28     
29            cout << endl
30                 << "Test Again?(Type y or n and Return): ";
31            cin >> ans;
32        } while ( (ans != 'N') && (ans != 'n') );
33     
34        cout << endl;
35
36        return 0;
37    }
38    //Uses iostream:
39    void inputData(int a[], int lastPlantNumber)
40    {
41        using namespace std;
42        for (int plantNumber = 1;
43             plantNumber <= lastPlantNumber; plantNumber++)
44        {
45             cout << endl
46                  << "Enter production data for plant number " 
47                   << plantNumber << endl;
48            getTotal(a[plantNumber − 1]);
49        }
50    }
51
52
53    //Uses iostream:
54    void getTotal(int& sum)
55    {
56        using namespace std;
57        cout << "Enter number of units produced by each department.\n"
58             << "Append a negative number to the end of the list.\n";
59     
60        sum = 0;
61        int next;
62        cin >> next;
63        while (next >= 0)
64        {
65            sum = sum + next;
66            cin >> next;
67        }
68     
69        cout << "Total = " << sum << endl;
70    }

Sample Dialogue

Enter production data for plant number 1
Enter number of units produced by each department.
Append a negative number to the end of the list. 
1 2 3 −1 
Total = 6

Enter production data for plant number 2
Enter number of units produced by each department.
Append a negative number to the end of the list. 
0 2 3 −1 
Total = 5

Enter production data for plant number 3
Enter number of units produced by each department.
Append a negative number to the end of the list. 
2 −1 
Total = 2
Enter production data for plant number 4
Enter number of units produced by each department.
Append a negative number to the end of the list.
−1 
Total = 0
Total production for each of plants 1 through 4:
6 5 2 0
Test Again?(Type y or n and Return): n
void scale(int a[], int size)
{
     for (int index = 0; index < size; index++)
         a[index] = roundNum(a[index]/1000.0 );
}

Notice that we divided by 1000.0, not by 1000 (without the decimal point). If we had divided by 1000, we would have performed integer division. For example, 2600/1000 would give the answer 2, but 2600/1000.0 gives the answer 2.6. It is true that we want an integer for the final answer after rounding, but we want 2600 divided by 1000 to produce 3, not 2, when it is rounded to a whole number.

We now turn to the definition of the function roundNum, which rounds its argument to the nearest integer. For example, roundNum(2.3) returns 2, and roundNum(2.6) returns 3. The code for the function roundNum, as well as that for scale, is given in Display 7.7. The code for round may require a bit of explanation.

The function roundNum uses the predefined function floor from the library with the header file cmath. The function floor returns the whole number just below its argument. For example, floor(2.1) and floor(2.9) both return 2. To see that roundNum works correctly, let’s look at some examples. Consider roundNum(2.4). The value returned is

floor(2.4 + 0.5)

which is floor(2.9), and that is 2.0. In fact, for any number that is greater than or equal to 2.0 and strictly less than 2.5, that number plus 0.5 will be less than 3.0, and so floor applied to that number plus 0.5 will return 2.0. Thus, round applied to any number that is greater than or equal to 2.0 and strictly less than 2.5 will return 2. (Since the function declaration for round specifies that the type for the value returned is int, the computed value of 2.0 is type cast to the integer value 2 without a decimal point using static_cast<int>.)

Now consider numbers greater than or equal to 2.5, for example, 2.6. The value returned by the call roundNum(2.6) is

floor(2.6 + 0.5)

Display 7.7 The Function scale

 1    //Demonstration program for the function scale.
 2    #include <iostream>
 3    #include <cmath>
 4     
 5    void scale(int a[], int size);
 6
   //Precondition: a[0] through a[size − 1] each has a nonnegative value.
 7   //Postcondition: a[i] has been changed to the number of 1000s (rounded to
 8    //an integer) that were originally in a[i], for all i such that 0 <= i <= size − 1.
 9     
10    int roundNum(double number);
11    //Precondition: number >= 0.
12    //Returns number rounded to the nearest integer.
13     
14    int main( )
15    {
16        using namespace std;
17        int someArray[4], index;
18        cout << "Enter 4 numbers to scale: ";
19        for (index = 0; index < 4; index++)
20            cin >> someArray[index];
21        scale(someArray, 4);
22        cout << "Values scaled to the number of 1000s are: ";
23        for (index = 0; index < 4; index++)
24            cout << someArray[index] << " ";
25        cout << endl;
26        return 0;
27    }
28     
29    void scale(int a[], int size)
30    {
31        for (int index = 0; index < size; index++)
32            a[index] = roundNum(a[index]/1000.0);
33    }
34     
35    //Uses cmath:
36    int roundNum(double number)
37    {
38        using namespace std;
39        return static_cast<int>(floor(number + 0.5));
40    }

Sample Dialogue

Enter 4 numbers to scale: 2600 999 465 3501
Values scaled to the number of 1000s are: 3 1 0 4

which is floor(3.1) and that is 3.0. In fact, for any number that is greater than or equal to 2.5 and less than or equal to 3.0, that number plus 0.5 will be greater than 3.0. Thus, roundNum called with any number that is greater than or equal to 2.5 and less than or equal to 3.0 will return 3.

Thus, roundNum works correctly for all arguments between 2.0 and 3.0. Clearly, there is nothing special about arguments between 2.0 and 3.0. A similar argument applies to all nonnegative numbers. So, roundNum works correctly for all nonnegative arguments.

Testing scale

Display 7.7 contains a demonstration program for the function scale, but the testing programs for the functions round and scale should be more elaborate than this simple program. In particular, they should allow you to retest the tested function several times rather than just once. We will not give the complete testing programs, but you should first test round (which is used by scale) in a driver program of its own, and then test scale in a driver program. The program to test round should test arguments that are 0, arguments that round up (like 2.6), and arguments that round down like 2.3. The program to test scale should test a similar variety of values for the elements of the array.

The Function graph

The complete program for producing the desired bar graph is shown in Display 7.8. We have not taken you step-by-step through the design of the function graph because it is quite straightforward.

Display 7.8 Production Graph Program

 1    //Reads data and displays a bar graph showing productivity for each plant.
 2    #include <iostream>
 3    #include <cmath>
 4    const int NUMBER_OF_PLANTS = 4;

 5    void inputData(int a[], int lastPlantNumber);
 6    //Precondition: lastPlantNumber is the declared size of the array a.
 7    //Postcondition: For plantNumber = 1 through lastPlantNumber:
 8    //a[plantNumber − 1] equals the total production for plant number plantNumber.

 9    void scale(int a[], int size);
10    //Precondition: a[0] through a[size − 1] each has a nonnegative value.
11    //Postcondition: a[i] has been changed to the number of 1000s (rounded to
12    //an integer) that were originally in a[i], for all i such that 0 <= i <= size − 1.

13    void graph(const int asteriskCount[], int lastPlantNumber);
14    //Precondition: asteriskCount[0] through asteriskCount[lastPlantNumber − 1]
15    //have nonnegative values.
16    //Postcondition: A bar graph has been displayed saying that plant
17    //number N has produced asteriskCount[N − 1] 1000s of units, for each N such that
18    //1 <= N <= lastPlantNumber
19    void getTotal(int& sum);
20    //Reads nonnegative integers from the keyboard and
21    //places their total in sum.
22    int roundNum(double number);
23    //Precondition: number >= 0.
24    //Returns number rounded to the nearest integer.
25    void printAsterisks(int n);
26    //Prints n asterisks to the screen.
27    int main( )
28    {
29        using namespace std;
30        int production[NUMBER_OF_PLANTS];
31        cout << "This program displays a graph showing\n"
32             << "production for each plant in the company.\n";
33        inputData(production, NUMBER_OF_PLANTS);
34        scale(production, NUMBER_OF_PLANTS);
35        graph(production, NUMBER_OF_PLANTS);
36        return 0;
37    }
38    //Uses iostream:
39    void inputData(int a[], int lastPlantNumber)
    <The rest of the definition of inputData is given in  Display 7.6.>
40    //Uses iostream:
41    void getTotal(int& sum)
    <The rest of the definition of getTotal is given in  Display 7.6.>
42    void scale(int a[], int size)
    <The rest of the definition of scale is given in  Display 7.7.>
43    //Uses cmath:
44    int roundNum(double number)
    <The rest of the definition of round is given in  Display 7.7.>
45    //Uses iostream:
46    void graph(const int asteriskCount[], int lastPlantNumber)
47    {                                                         
48        using namespace std;                                  
49        cout << "\nUnits produced in thousands of units:\n";  
50        for (int plantNumber = 1;                             
51             plantNumber <= lastPlantNumber; plantNumber++)   
52        {                                                     
53             cout << "Plant #" << plantNumber << " ";         
54             printAsterisks(asteriskCount[plantNumber − 1]);  
55             cout << endl;                                    
56        }                                                     
57    }                                                         
58    //Uses iostream:
59    void printAsterisks(int n)
60    {
61        using namespace std;
62        for (int count = 1; count <= n; count++)
63            cout << "*";
64    }

Sample Dialogue

This program displays a graph showing
production for each plant in the company.
Enter production data for plant number 1
Enter number of units produced by each department.
Append a negative number to the end of the list.
2000 3000 1000 −1
Total = 6000
Enter production data for plant number 2
Enter number of units produced by each department.
Append a negative number to the end of the list.
2050 3002 1300 −1
Total = 6352
Enter production data for plant number 3
Enter number of units produced by each department.
Append a negative number to the end of the list.
5000 4020 500 4348 −1
Total = 13868
Enter production data for plant number 4
Enter number of units produced by each department.
Append a negative number to the end of the list.
2507 6050 1809 −1
Total = 10366
Units produced in thousands of units: Plant #1 ******
Plant #2 ******
Plant #3 **************
Plant #4 **********

Self-Test Exercises

  1. Write a function definition for a function called one_more, which has a formal parameter for an array of integers and increases the value of each array element by one. Add any other formal parameters that are needed.

  2. Consider the following function definition:

    void too2(int a[ ], int howMany)
    {
        for (int index = 0; index < howMany; index++)
            a[index] = 2;
    }

    Which of the following are acceptable function calls?

    int myArray[29];
    too2(myArray, 29);
    too2(myArray, 10);
    too2(myArray, 55);
    "Hey too2. Please, come over here."
    int yourArray[100];
    too2(yourArray, 100);
    too2(myArray[3], 29);
  3. Insert const before any of the following array parameters that can be changed to constant array parameters:

    void output(double a[], int size);
    //Precondition: a[0] through a[size − 1] have values.
    //Postcondition: a[0] through a[size − 1] have been
    //written out.
    void dropOdd(int a[ ], int size);
    //Precondition: a[0] through a[size − 1] have values.
    //Postcondition: All odd numbers in a[0] through
    //a[size − 1] have been changed to 0.
  4. Write a function named outOfOrder that takes as parameters an array of double s and an int parameter named size and returns a value of type int. This function will test this array for being out of order, meaning that the array violates the following condition:

    a[0] <= a[1] <= a[2] <= ...

    The function returns −1 if the elements are not out of order; otherwise, it will return the index of the first element of the array that is out of order. For example, consider the declaration

    double a[10] = {1.2, 2.1, 3.3, 2.5, 4.5,
                      7.9, 5.4, 8.7, 9.9, 1.0};

    In this array, a[2] and a[3] are the first pair out of order, and a[3] is the first element out of order, so the function returns 3. If the array were sorted, the function would return −1.