Computer Science 111, Assignment 5
Tutorial on one-dimensional arrays

Before you read this tutorial, it is recommended that you first read Skansholm, § 2.7, on arrays. This tutorial will develop a few points further, but will not cover everything you need to know about arrays. You will find this particular tutorial much easier to understand if you have read the textbook first. (On the other hand, most of the other CS 111 tutorials are probably best read before the corresponding textbook sections.)


  1. Arrays.   An array is a sequence of variables which have the same name except for a number in square brackets known as the index. The indexed variables represent memory locations that are contiguous (next to each other) in memory. The individual indexed variables are known as elements of the array. The indices range from 0 up to one less than the number of elements. The number of elements is called the length of the array.

    You have already used two particular kinds of arrays quite often:  (1) C-strings, which are arrays of characters, and (2) the array of command-line arguments, an array of C-strings. Note that the array of command-line arguments is an array of arrays of characters, also known as a two-dimensional array of characters.

    It is possible to have arrays of any data type. An array of any kind of data type other than another array type is known as a one-dimensional array. This tutorial will focus on one-dimensional arrays only. For now, we will consider arrays of primitive data types such as int and float.

    For example, to create an array of five float memory locations, to contain five floating-point numbers, we can declare the array as follows:

       flost numbers[5];
    

    Having declared our array, we now need to put values in it. Values are put into an array one location at a time, usually using a loop. For example, to put a value of zero in all the elements (memory locations) in the above array:

       for ( int i = 0; i < 5; i++ )
          numbers[i] = 0;
    

    Instead of using the numeric literal 5 twice, as we did above, it is better programming practice to use a named constant to keep track of the length of the array, to minimize the number of changes we would have to make if we later need to edit the program to change the length of the array.

       const int LENGTH = 5;
       float numbers[LENGTH];
       for ( int i = 0; i < LENGTH; i++ )
          numbers[i] = 0;
    

    When writing a program that uses arrays, we must be careful to avoid accessing locations with indices beyond the bounds of the array, i.e. indices less than zero or greater than the length minus one. Unfortunately, if you go beyond the bounds of an array, the compiler doesn't complain. Instead, your program will wreak unpredictable havoc with memory locations outside the array -- mamory locations that have been allocated for other purposes, e.g. other variables, or, worse yet, the machine-code instructions of the program itself.

    Viruses and other malicious software will, often, go deliberately beyond the bounds of an array. Normal programs should not.

    To avoid going beyond the bounds of an array, you should get in the habit of accessing array elements using for loops which, as in the above example, initialize the loop control variable to zero, increment it by 1 each time through the loop, up to but not including the length of the array. In the above for loop. note the use of less-than (<) rather than less-than-or-equal (<=).

    Alas, arrays do not remember their own length. So, we need other means, such as the separate integer constant we used above, to keep track of the number of elements in the array. In the case of C-strings (arrays of characters), it is customary to use an extra location within the array itself to contain a special character, the null character, to indicate the end of the string, instead of using a separate constant.


  2. Example program: Curving exam scores. We will now look at an example program involving an array of integers. Compile examScores1.cpp and run it. First, run it without command line arguments. Then, run it for any number of command-line arguments which are integers greater than or equal to zero and less than 100. The command-line arguments are supposed to represent exam scores. The program displays a table which lists both the original exam scores and curved exam scores.

    The curve is calculated by raising the highest score to 100 and raising all others by the same amount. To accomplish this, it is necessary to find the maximum exam score, then calculate the difference between 100 and the maximum, then raise all the exam scores by that amount. Thus, it is necessary to keep track of all the exam scores, so they can be accessed again after calculating the difference between 100 and the maximum. Hence the need to store the exam scores in an array.

    Look now at the source code. The exam scores are stored in an array of int variables. To create the array, we declare it as follows:

       int scores[NUMBER_OF_SCORES];
    

    where scores is the name of the array, and NUMBER_OF_SCORES was previously declared as a constant of type int. More generally, in any array declaration, the expression in square brackets after the name of the array can be any expression of type int, and this expression determines the length of the array, i.e. the number of elements (memory locations) that the array contains.

    Our program will now put exam scores into the array, reading them from the command-line arguments. The following loop reads argv[1] into scores[0], argv[2] into scores[1], and so on -- and does some other things too, such as finding the maximum score:

       int maximumScore = 0;   // Because no score is less than 0,
                               // the maximum score >= 0.
    
       for ( int i = 0; i < NUMBER_OF_SCORES; i++ )  {
          if ( ! sscanf(argv[i+1], "%d", &scores[i]) )  {
             cout << "Scores must be integers only." << endl;
             return 1;
          }  // if
    
          if ( scores[i] > maximumScore )
             maximumScore = scores[i];
       } // for i
    

    Recall that NUMBER_OF_SCORES is the number of elements in the array scores. The index of an array element can range from zero up to one less than the length.

    In our present example, the maximum score is determined by comparing the variable maximumScore to all the scores. If a score is encoutered which is larger than the maximum so far, maximumScore is then raised to the new maximum. To ensure that maximumScore is no larger than the actual maximum of all the scores, even if everyone got a zero on the exam, maximumScore is initialized to zero before the loop.


  3. Partially-filled arrays.   Compile, run, and examine examScores2.cpp, a program similar to examScores1.cpp except that it uses text file I/O. It reads a list of exam scores from an input data file examScores.txt, determines the maximum score, and then prints, to an output file curve.txt, both (1) the original scores, and (2) the scores curved up so that the maximum score becomes 100.

    As the scores are read from the input file, they are stored in an array of integers. It is not known in advance how many scores there will be in ExamScores.txt, except that there will be no more than 40 scores. Hence the array is declared with length 40.

    But instead of just using the number 40 directly, we declare it at the top of our main find as a named constant, to which we have given the name MAX_NUMBER_OF_SCORES. The filenames have likewise been declared as string constants, to make the program easier to modify.

    Note that the array is partially-filled. Because it is not known in advance how many scores there will be, not all the locations in the array will necessarily contain actual scores. When printing the scores and the curved scores to the output file, we must be careful to avoid reading from those locations in the array that do not contain scores. Hence we must keep track of how many scores have been put in the array. The variable numberOfScores is used to count how many scores are in the array, and hence to determine how many locations should be read from when the scores are printed to the output file.

    Look also at examScores3.cpp, which is similar to examScores2.cpp except that it uses a while loop with a break statement.

    Then look at examScores4.cpp, which is similar to examScores3.cpp except that it checks for the possibility that the array may not be big enough to hold the contents of the file, and in that case prints an error message and quits.


  4. Initialization of one-dimensional arrays.   In C++, an initialization is a combination of declaration and assignment. For example, a variable x, of type float, could be initialized to the value 3.7 as follwos:

       float x = 3;
    

    thereby combining the following declaration and assignment:

       float x;    // declaration
       c = 3;      // assignment
    

    We can also initialize arrays. The following initialization of an array:

       int array[] = {0, 2, 4, 6, 8};
    

    is equivalent to the following declaration and assignment of values to all the elements:

       int array[5];
       for ( int i = 0; i < 5; i++ )
          array1[i] = 2*i;
    

    To initialize an array, we typically use an initializer list, consisting of all the values, separated by commas and enclosed in curly braces. Note that it is not necessary to specify the size of the array in square brackets, because the compiler can count how many items are in the initializer list. (Thus, an array created via an initializer list will be completely filled.) However, although the compiler can count how many elements the array contains, it does not automatically keep track to this information for the sake of subsequent statements in the program. So, we must still keep track of the length of the array by some separate means, e.g. by declaring an integer constant:

       int array[] = {0, 2, 4, 6, 8};
       const int LENGTH = 5;
    

    A C-string, i.e. an array of characters, can also be initialized using a string literal, rather than an initializer list. For example:

       char word[] = "hello";
    

    This is equivalent to:

       char word[] = {'H', 'e', 'l', 'l', 'o', '\0'};
    

    where '\0' is a literal for the null character which marks the end of a C-string.. Thus, if you use a string literal to initialize a C-string, the length of the resulting array must be one greater than the number of characters in the string literal, to have one extra memory location for the null character.


  5. Operations on arrays.   Except for initializations, we cannot change or otherwise access the contents of an array all at once. We can only access the elements of an array one at a time. For example, suppose we've created an array of long integers a as follows:

       long a[] = { 123456789, 987654321, 112233445566778899 };
       const int LENGTH = 3;
    

    Suppose we now want to add 2 to every element in a. We cannot simply write:

       a += 2;
    

    Instead, we must individually change all the elemnents in a, one at a time:

       for ( int i = 0; i < LENGTH; i++ )
          a[i] += 2;
    

    Likewise, suppose we've created another array b, having the same length as a:

       long b[LENGTH];
    

    Suppose we now want to copy the entire contents of array a into array b. We CANNOT simply write:

       b = a;
    

    which would be a syntax error. Instead, we must copy the elements one at a time, as follows:

       for ( int i = 0; i < LENGTH; i++ )
          b[i] = a[i];
    

    However, one thing we CAN do with an entire array, all at once, is pass it as an argument to a function. For example, suppose we have a function with the following heading:

    myFunction(long myArray[], int length)
    

    To call this function, passing our previously-defined array a as an argument, we would write:

       myFunction(a, LENGTH)
    

    Note the need for a separate parameter for the length, because the array cannot, by itself, remember its own length (unless the array contains an extra location for a sentinel value, as a C-string does).


Back to: