Lab 11 -- More on Arrays

In this lab we will learn about

Using a Defined Constant for the Size of an Array

In many cases we take a good guess at the size of an array, but have to modify the program later when we find out that the array needs to be larger or smaller than our guess. It is much easier to make such a change if the original programmer used a defined constant for the size of the array. Here is an example of an easy-to-modify program:
//p1.C
#include <iostream.h>
#include <iomanip.h>
const int SIZE = 8;
int main(void)
{
        double average;
        int numlist[SIZE];
        int i, sum=0;
        for (i=0; i<SIZE; ++i)
        {
                cout << "Enter value #" << i+1 << ": ";
                cin >> numlist[i];
        }
        cout << endl;
        for (i=0; i<SIZE; ++i)
                cout << "Value #" << i+1 << " is " << numlist[i] << endl;
        for (i=0; i<SIZE; ++i)
                sum = sum + numlist[i];
        average = (double)sum/SIZE;
        cout.setf(ios::fixed);
        cout.setf(ios::showpoint);
        cout << "\nThe average is " << setprecision(2) << average << endl;
        return 0;
}
Copy the program to p1.C and compile and run it. Now suppose that we need to find the average of 12 grades instead of 8. All we have to do is change the 8 in the initialization of SIZE to 12. If we had not used a defined constant, we would have to find every occurrence of 8 in the program and change it to 12. It is easy to miss an occurrence of 8 when doing such a change and thus create a logical error in the program.

Make the change of SIZE from 8 to 12 and rerun the program. See how easy it was to change it?

Passing a Whole Array to a Function

We have seen call-by-value parameters and call-by-reference parameters. A parameter for an entire array is a third kind of parameter, aptly named an array parameter. An array parameter is like a call-by-reference parameter in that any change made to the array within the function actually changes the array that was passed to the function (in main or some other calling function.) However, it is not necessary to use the ampersand on an array parameter. Instead, we put square brackets after the name of the array parameter. Here is a function that could get values for numlist:
void get_values(int list[], int size)
{
        int i;
        for (i=0; i<size; ++i)
        {
                cout <<"Enter value: ";
                cin >> list[i];
        }
}
You might think that an error was made in the above function definition because there is no number within the square brackets after the array name. That is not an error. We use empty square brackets to indicate that the parameter is an array parameter. If we put a number in the brackets, the compiler will not give us an error but it will ignore the number.

Notice that we have a second parameter, size, in the function above. This parameter is necessary because when C++ passes an entire array to a function, it does not inform the function of the array's size. It tells the function where the beginning of the array is in memory and that's all. The function call must include both the name of the array and the size of the array, like this:

get_values(numlist, 8);
or
get_values(numlist, SIZE); // if we used a defined constant
Notice in both of these calls, we do not put square brackets after the name of the array. When sending an entire array to a function, do not use square brackets.

It might seem like an inconvenience to have to include a separate parameter for the size of the array. But doing so makes a function usable for arrays of different sizes. Study the following program:

//p2.C
#include <iostream.h>
const int SIZE_OF_SECTION = 5;
const int SIZE_OF_GRADES = 10;
void get_values(int list[], int size);
int main (void)
{
        int section[SIZE_OF_SECTION];
        int grades[SIZE_OF_GRADES];
        int i;
        cout << "First enter a student ID number for each of "
             << SIZE_OF_SECTION << " students:\n";
        get_values(section, SIZE_OF_SECTION);
        cout << "\n\nNow enter two grades for each student:\n";
        get_values(grades, SIZE_OF_GRADES);
        cout.setf(ios::fixed);
        cout.setf(ios::showpoint);
        cout.precision(2);
        cout << endl;
        for (i=0; i<SIZE_OF_SECTION; ++i)
        {
                cout << "The average score for student #" << section[i];
                << " is " << (grades[2*i] + grades[2*i+1])/2.0
                << endl;
        }
}
void get_values(int list[], int size)
{
        int i;
        for (i=0; i<size; ++i)
        {
                cout << "Enter value: ";
                cin >> list[i];
        }
}
Copy the program to a file called p2.C and run it. Enter ID numbers 3125, 4268, 5176, 5829, and 6013. Then enter grades 80, 88, 92, 95, 75, 60, 82, 97, 65, and 70. Does the output appear to be correct? It should be. Study the cout statement that is calculating the average. Notice the indexes 2*i and 2*i+1. Can you explain how these indexes allow the program to add the two grades for each student? Try to do so before reading the next paragraph.

The variable i in the for loop in main varies from 0 to 4. When i is 0, 2*i is 0 and 2*i+1 is 1. The first student's two grades are stored in grades[0] and grades[1]. When i is 1, 2*i is 2 and 2*i+1 is 3. The second student's two grades are stored in grades[2] and grades[3]. Our index formulas work correctly.

Using const with an Array Parameter

If you place the reserved word const in front of an array parameter, the function cannot change the value of any element of the array. If it tries to do so, the compiler will report a syntax error.

This is a good idea if you are writing a function that will be used by other programmers and you want to make it clear to them that your function will not make any changes to the array it is passed. A prototype such as

double average(const int list[], int size);
// This function returns the average of all the elements in the array.
tells you that this function must be sent an integer array and an integer size when it is called, and that the function returns a double. In addition, it assures you that the values in any array passed to this function will not be changed by the function.

Partially Filled Arrays

Sometimes we don't know in advance how many values we will have to store in an array. In such a case, we declare the array to be of a size large enough to contain the maximum number of values we expect to need. Then, as we fill the array, we count the number of values that we get and use that count as the size of the array.

Soon you will see an example of a partially filled array. First, let me describe a program that requires using this example.

Suppose we want to write a program to add very large integers. Our C++ compiler's integer data type is stored in 4 bytes. This means that the largest integer we can store in an integer variable is approximately 2 billion. If we need larger integers, we will have to develop a new way of storing them.

One way to store a large integer is to store the digits of the integer in a character array. Let's say that we will never need integers with more than 20 digits. Then we can declare an array of size 20 and be sure that any of our large integers will fit into it. As we fill the array with digit characters, we will count the digits so that we know how much of the array we are currently using. Here is the example:

//p3.C
#include <iostream.h>
#include <ctype.h>
const int MAXSIZE = 20;
int main(void)
{
        char digit_array[MAXSIZE], digit;
        int size, i;
        size = 0;
        cout << "Enter an integer with no more than 20 digits: ";
        do {
                cin.get(digit);
                if (isdigit(digit))
                {
                        digit_array[size] = digit;
                        ++size;
                }
        } while (size < 20 && isdigit(digit));
        cout << "The integer you entered is: ";
        for (i=0; i<size; ++i)
                cout << digit_array[i];
        cout << endl;
        return 0;
}
Copy this program to p3.C and run it. Does it behave as specified? What happens if you accidentally put in a letter somewhere in the integer? What happens if you put in more than 20 digits?

Now let's suppose that we need to reverse the digits of the integer so that the last digit becomes the first digit, the next-to-last becomes the second, and so forth.

Let's write a function that reverses a character array. We will pass the function a character array and the size of the array. Let's call the array parameter chlist (for character list.) The function will contain another character array as a local variable into which it will copy chlist. The function will copy chlist right to left but will put the copy into its other array in left to right order. Then it will copy the contents of this other array back into chlist from left to right.

Try to write such a function below the main in p6.C. Call the function reverse and give it the following prototype:

void reverse(char chlist[], int size);
// This function reverses the contents of chlist.
Once you have written the function, put a call to the function in main below the for loop. After the call, add another copy of the for loop so that the array will be printed again. If your function works correctly, the second printing should be backward.

//p4.C

//This program illustrates the initialization of array of characters.
//Also shows the difference between the way an array is passed to a function.

#include<iostream.h>

void func( char x[]) //Note how it is passed

{
    cout << "I am printing the array a inside func \n";
    cout << x << "\n";
}

void func2( char x[4]) //Note how it is passed

{
    cout << "I am printing the array a inside func2 \n";
    cout << x << "\n";
}

void func3( char x[5]) //Note how it is passed

{
    cout << "I am printing the array a inside func3 \n";
    cout << x << "\n";
}

int main()

{
    char a[12] = "bookstores";

    cout << "I am printing string a before I call func \n";

    cout << a << "\n";
    func(a);
    func2(a);
    func3(&a[4]);

    cout << "I am printing string after I called func, func2, func3 \n";

    cout << a << "\n";
 
    //Intialization of arrays

    char n0[11]= {'B','r','o','o','k','s'};

    char n1[11]="Tashakkori";
    char n2[11];

    cout << "array n0 is " << n0 <<"\n" ;

    cout << "array n1 is " n1 << "\n";
 
    cout << "Now start entering characters, then press enter \n";
    cout << "To terminate enter null, Ctrl-D \n\n";
 
    int i =0;
    cin.get(n2[i]);
    i++;
 

    while( cin.get(n2[i]) != '\0')

      i++;

    cout << "\n The last array is " << n2 <<"\n";

    return 0;
}
 
Searching and Sorting Arrays
We will not deal with the topics of searching and sorting in this lab, but they are very important topics in computer science. Our text does a good job of explaining them. Be sure you read about them in Chapter (9) of the book.

Lab-work:
Modify the func3 function such that the string becomes bookstore instead of bookshelf. Hint: Use the string "store" to replace "shelf" at the end of the long string that is holding the bookshelf.