7.4 Examples Using arrays

The following examples demonstrate how to declare, initialize and manipulate arrays.

7.4.1 Declaring an array and Using a Loop to Initialize the array’s Elements

The program in Fig. 7.3 declares five-element integer array n (line 9). Line 5 includes the <array> header, which contains the definition of class template array. Lines 12–14 use a for statement to initialize the array elements to zeros. Like other non-static local variables, arrays are not implicitly initialized to zero (static arrays are). The first output statement (line 16) displays the column headings for the columns printed in the subsequent for statement (lines 19–21), which prints the array in tabular format. Remember that setw specifies the field width in which only the next value is to be output.

Fig. 7.3 Initializing an array’s elements to zeros and printing the array.

Alternate View

 1   // Fig. 7.3: fig07_03.cpp
 2   // Initializing an array's elements to zeros and printing the array.
 3   #include <iostream>
 4   #include <iomanip>
 5   #include <array>
 6   using namespace std;
 7
 8   int main() {
 9      array<int, 5> n; // n is an array of 5 int values
10
11      // initialize elements of array n to 0        
12      for (size_t i{0}; i < n.size(); ++i) {        
13         n[i] = 0; // set element at location i to 0
14      }                                             
15
16      cout << "Element" << setw(10) << "Value" << endl;
17
18      // output each array element's value                
19      for (size_t j{0}; j < n.size(); ++j) {              
20         cout << setw(7) << j << setw(10) << n[j] << endl;
21      }                                                   
22 }

Element Value
      0     0
      1     0
      2     0
      3     0
      4     0

In this program, the control variables i (line 12) and j (line 19) that specify array subscripts are declared to be of type size_t. According to the C++ standard size_t represents an unsigned integral type. This type is recommended for any variable that represents an array’s size or an array’s subscripts. Type size_t is defined in the std namespace and is in header <cstddef>, which is included by various other headers. If you attempt to compile a program that uses type size_t and receive errors indicating that it’s not defined, simply add #include <cstddef> to your program.

7.4.2 Initializing an array in a Declaration with an Initializer List

The elements of an array also can be initialized in the array declaration by following the array name with a brace-delimited comma-separated list of initializers. The program in Fig. 7.4 uses an initializer list to initialize an integer array with five values (line 9) and prints the array in tabular format (lines 11–16).

Fig. 7.4 Initializing an array in a declaration.

Alternate View

 1   // Fig. 7.4: fig07_04.cpp
 2   // Initializing an array in a declaration.
 3   #include <iostream>
 4   #include <iomanip>
 5   #include <array>
 6   using namespace std;
 7
 8   int main() {
 9      array<int, 5> n{32, 27, 64, 18, 95}; // list initializer
10
11      cout << "Element" << setw(10) << "Value" << endl;
12
13      // output each array element's value
14      for (size_t i{0}; i < n.size(); ++i) {
15         cout << setw(7) << i << setw(10) << n[i] << endl;
16      }
17    }

Element   Value
      0      32
      1      27
      2      64
      3      18
      4      95

Fewer Initializers Than array Elements

If there are fewer initializers than array elements, the remaining array elements are initialized to zero. For example, the elements of array n in Fig. 7.3 could have been initialized to zero with the declaration


array<int, 5> n{}; // initialize elements of array n to 0

which initializes the elements to zero, because there are fewer initializers (none in this case) than array elements. This technique can be used only in the array’s declaration, whereas the initialization technique shown in Fig. 7.3 can be used repeatedly during program execution to “reinitialize” an array’s elements.

More Initializers Than array Elements

If an initializer list is specified in an array declaration, the number of initializers must be less than or equal to the array size. The array declaration


array<int, 5> n{32, 27, 64, 18, 95, 14};

causes a compilation error, because there are six initializers and only five array elements.

7.4.3 Specifying an array’s Size with a Constant Variable and Setting array Elements with Calculations

Figure 7.5 sets the elements of a 5-element array named values to the even integers 2, 4, 6, 8 and 10 (lines 14–16) and prints the array in tabular format (lines 18–23). These numbers are generated (line 15) by multiplying each successive value of the loop counter, i, by 2 and adding 2.

Fig. 7.5 Set array s to the even integers from 2 to 10.

Alternate View

 1   // Fig. 7.5: fig07_05.cpp
 2   // Set array s to the even integers from 2 to 10.
 3   #include <iostream>
 4   #include <iomanip>
 5   #include <array>
 6   using namespace std;
 7
 8   int main() {
 9      // constant variable can be used to specify array size
10      const size_t arraySize{5}; // must initialize in declaration
11
12      array<int, arraySize> values; // array values has 5 elements
13
14      for (size_t i{0}; i < values.size(); ++i) { // set the values
15         values[i] = 2 + 2 * i;
16      }
17
18      cout << "Element" << setw(10) << "Value" << endl;
19
20      // output contents of array s in tabular format
21      for (size_t j{0}; j < values.size(); ++j) {
22         cout << setw(7) << j << setw(10) << values[j] << endl;
23      }
24   }

Element Value
      0     2
      1     4
      2     6
      3     8
      4    10

Constant Variables

Line 10 uses the const qualifier to declare a constant variable arraySize with the value 5. Constant variables are also called named constants or read-only variables. A constant variable must be initialized when it’s declared and cannot be modified thereafter. Attempting to modify arraySize after it’s initialized, as in


arraySize = 7;

results in the following errors1 from Visual C++, GNU C++ and LLVM, respectively:

  • Visual C++: 'arraySize': you cannot assign to a variable that is const

  • GNU C++: error: assignment of read-only variable ‘x’

  • LLVM: Read-only variable is not assignable

Common Programming Error 7.2

Not initializing a constant variable when it’s declared is a compilation error.

 

Common Programming Error 7.3

Assigning a value to a constant variable in a separate statement from its declaration is a compilation error.

 

Good Programming Practice 7.1

Defining the size of an array as a constant variable instead of a literal constant makes programs clearer and easier to update. This technique eliminates so-called magic numbers—numeric values that are not explained. Using a constant variable allows you to provide a name for a literal constant and can help explain the purpose of the value in the program.

7.4.4 Summing the Elements of an array

Often, the elements of an array represent a series of values to be used in a calculation. For example, if the elements of an array represent exam grades, a professor may wish to total the elements of the array and use that sum to calculate the class average for the exam.

The program in Fig. 7.6 sums the values contained in the four-element integer array a. The program declares, creates and initializes the array in line 9. The for statement (lines 13–15) performs the calculations. The values being supplied as initializers for array a also could be read into the program—for example, from the user at the keyboard or from a file on disk (see Chapter 14, File Processing). For example, the for statement


for (size_t j{0}; j < a.size(); ++j) {
   cin >> a[j];
}

reads one value at a time from the keyboard and stores the value in element a[j].

Fig. 7.6 Compute the sum of the elements of an array.

Alternate View

 1   // Fig. 7.6: fig07_06.cpp
 2   // Compute the sum of the elements of an array.
 3   #include <iostream>
 4   #include <array>
 5   using namespace std;
 6
 7   int main() {
 8      const size_t arraySize{4}; // specifies size of array
 9      array<int, arraySize> a{10, 20, 30, 40};
10      int total{0};
11
12      // sum contents of array a
13      for (size_t i{0}; i < a.size(); ++i) {
14         total += a[i];
15      }
16
17      cout << "Total of array elements: " << total << endl;
18   }

Total of array elements: 100

7.4.5 Using a Bar Chart to Display array Data Graphically

Many programs present data to users graphically. For example, numeric values are often displayed as bars in a bar chart, with longer bars representing proportionally larger numeric values. One simple way to display numeric data graphically is with a bar chart that shows each numeric value as a bar of asterisks (*).

Professors often like to examine grade distributions on an exam. A professor might graph the number of grades in each of several categories to visualize the grade distribution. Suppose the grades were 87, 68, 94, 100, 83, 78, 85, 91, 76 and 87. There was one grade of 100, two grades in the 90s, four grades in the 80s, two grades in the 70s, one grade in the 60s and no grades below 60. Our next program (Fig. 7.7) stores this data in an array of 11 elements, each corresponding to a grade range. For example, n[0] indicates the number of grades in the range 0–9, n[7] indicates the number of grades in the range 70–79 and n[10] indicates the number of grades of 100. The GradeBook classes that you’ll see in Figs. 7.13 and 7.19 contain code that calculates grade frequencies based on a set of grades. In this example, we initialize the array n with frequency values.

The program (Fig. 7.7) reads the numbers from the array and graphs the information as a bar chart, displaying each grade range followed by a bar of asterisks indicating the number of grades in that range. To label each bar, lines 17–25 output a grade range (e.g., "70-79: ") based on the current value of counter variable i. The nested for statement (lines 28–30) outputs the current bar as the appropriate number of asterisks. Note the loop-continuation condition in line 28 (stars < n[i]). Each time the program reaches the inner for, the loop counts from 0 up to n[i], thus using a value in array n to determine the number of asterisks to display. In this example, n[0]n[5] contain zeros because no students received a grade below 60. Thus, the program displays no asterisks next to the first six grade ranges.

Fig. 7.7 Bar chart printing program.

Alternate View

 1   // Fig. 7.7: fig07_07.cpp
 2   // Bar chart printing program.
 3   #include <iostream>
 4   #include <iomanip>
 5   #include <array>
 6   using namespace std;
 7
 8   int main() {
 9      const size_t arraySize{11};
10      array<unsigned int, arraySize> n{0, 0, 0, 0, 0, 0, 1, 2, 4, 2, 1};
11
12      cout << "Grade distribution:" << endl;
13
14      // for each element of array n, output a bar of the chart
15      for (size_t i{0}; i < n.size(); ++i) {
16         // output bar labels ("0-9:", ..., "90-99:", "100:")
17         if (0 == i) {
18            cout << " 0-9: ";
19         }
20         else if (10 == i) {
21            cout << " 100: ";
22         }
23         else {
24            cout << i * 10 << "-" << (i * 10) + 9 << ": ";
25         }
26
27         // print bar of asterisks
28         for (unsigned int stars{0}; stars < n[i]; ++stars) {
29            cout << '*';
30         }
31
32         cout << endl; // start a new line of output
33     }
34   }

Grade distribution:
  0-9:
10-19:
20-29:
30-39:
40-49:
50-59:
60-69: *
70-79: **
80-89: ****
90-99: **
  100: *

7.4.6 Using the Elements of an array as Counters

Sometimes, programs use counter variables to summarize data, such as the results of a survey. In Fig. 6.7, we used separate counters in our die-rolling program to track the number of occurrences of each side of a die as the program rolled the die 60,000,000 times. An array version of this program is shown in Fig. 7.8. This version also uses the new C++11 random-number generation capabilities that were introduced in Section 6.9.

Figure 7.8 uses the array frequency (line 17) to count the occurrences of die value. The single statement in line 21 of this program replaces the entire switch statement in lines 22–43 of Fig. 6.7. Line 21 uses a random value to determine which frequency element to

Fig. 7.8 Die-rolling program using an array instead of switch.

Alternate View

 1   // Fig. 7.8: fig07_08.cpp
 2   // Die-rolling program using an array instead of switch.
 3   #include <iostream>
 4   #include <iomanip>
 5   #include <array>
 6   #include <random>
 7   #include <ctime>
 8   using namespace std;
 9
10   int main() {
11      // use the default random-number generation engine to
12      // produce uniformly distributed pseudorandom int values from 1 to 6
13      default_random_engine engine(static_cast<unsigned int>(time(0)));
14      uniform_int_distribution<unsigned int> randomInt(1, 6);
15
16      const size_t arraySize{7}; // ignore element zero
17      array<unsigned int, arraySize> frequency{}; // initialize to 0s
18
19      // roll die 60,000,000 times; use die value as frequency index
20      for (unsigned int roll{1}; roll <= 60'000'000; ++roll) {
21         ++frequency[randomInt(engine)];
22      }
23
24      cout << "Face" << setw(13) << "Frequency" << endl;
25
26      // output each array element's value
27      for (size_t face{1}; face < frequency.size(); ++face) {
28         cout << setw(4) << face << setw(13) << frequency[face] << endl;
29      }
30   }

Face Frequency
   1   9997901
   2   9999110
   3  10001172
   4  10003619
   5   9997606
   6  10000592

increment during each iteration of the loop. The calculation in line 21 produces a random subscript from 1 to 6, so array frequency must be large enough to store six counters. We use a seven-element array in which we ignore frequency[0]—it’s clearer to have the die face value 1 increment frequency[1] than frequency[0]. Thus, each face value is used directly as a subscript for array frequency. We also replace lines 46–51 of Fig. 6.7 by looping through array frequency to output the results (Fig. 7.8, lines 27–29).

7.4.7 Using arrays to Summarize Survey Results

Our next example uses arrays to summarize the results of data collected in a survey. Consider the following problem statement:

  • Twenty students were asked to rate on a scale of 1 to 5 the quality of the food in the student cafeteria, with 1 being “awful” and 5 being “excellent.” Place the 20 responses in an integer array and determine the frequency of each rating.

This is a popular type of array-processing application (Fig. 7.9). We wish to summarize the number of responses of each rating (that is, 1–5). The array responses (lines 14–15) is a 20-element integer array of the students’ responses to the survey. The array responses is declared const, as its values do not (and should not) change. We use a six-element array frequency (line 18) to count the number of occurrences of each response. Each element of the array is used as a counter for one of the survey responses and is initialized to zero. As in Fig. 7.8, we ignore frequency[0].

Fig. 7.9 Poll analysis program.

Alternate View

 1   // Fig. 7.9: fig07_09.cpp
 2   // Poll analysis program.
 3   #include <iostream>
 4   #include <iomanip>
 5   #include <array>
 6   using namespace std;
 7
 8   int main() {
 9      // define array sizes
10      const size_t responseSize{20}; // size of array responses
11      const size_t frequencySize{6}; // size of array frequency
12
13      // place survey responses in array responses
14      const array<unsigned int, responseSize> responses{
15         1, 2, 5, 4, 3, 5, 2, 1, 3, 1, 4, 3, 3, 3, 2, 3, 3 2, 2, 5};
16
17      // initialize frequency counters to 0
18      array<unsigned int, frequencySize> frequency{};
19
20      // for each answer, select responses element and use that value
21      // as frequency subscript to determine element to increment
22      for (size_t answer{0}; answer < responses.size(); ++answer) {
23         ++frequency[responses[answer]];
24      }
25
26      cout << "Rating" << setw(12) << "Frequency" << endl;
27
28      // output each array element's value
29      for (size_t rating{1}; rating < frequency.size(); ++rating) {
30         cout << setw(6) << rating << setw(12) << frequency[rating] << endl;
31      }
32   }

Rating Frequency
     1         3
     2         5
     3         7
     4         2
     5         3

The first for statement (lines 22–24) takes the responses one at a time from the array responses and increments one of the five counters in the frequency array (frequency[1] to frequency[5]). The key statement in the loop is line 23, which increments the appropriate frequency counter, depending on the value of responses[answer].

Let’s consider several iterations of the for loop. When control variable answer is 0, the value of responses[answer] is the value of responses[0] (i.e., 1 in line 15), so the program interprets ++frequency[responses[answer]] as


++frequency[1]

which increments the value in array element 1. To evaluate the expression in line 23, start with the value in the innermost set of square brackets (answer). Once you know answer’s value (which is the value of the control variable in line 22), plug it into the expression and evaluate the expression in the next outer set of square brackets (i.e., responses[answer], which is a value selected from the responses array in lines 14–15). Then use the resulting value as the subscript for the frequency array to specify which counter to increment.

When answer is 1, responses[answer] is the value of responses[1], which is 2, so the program interprets ++frequency[responses[answer]] as


++frequency[2]

which increments array element 2.

When answer is 2, responses[answer] is the value of responses[2], which is 5, so the program interprets ++frequency[responses[answer]] as


++frequency[5]

which increments array element 5, and so on. Regardless of the number of responses processed in the survey, the program requires only a six-element array (ignoring element zero) to summarize the results, because all the response values are between 1 and 5 and the subscript values for a six-element array are 0 through 5.

Bounds Checking for array Subscripts

If the data in responses contained an invalid value, such as 13, the program would have attempted to add 1 to frequency[13], which is outside the bounds of the array. When you use the [] operator to access an array element, C++ provides no automatic array bounds checking to prevent you from referring to an element that does not exist. Thus, an executing program can “walk off” either end of an array without warning. In Section 7.10, we demonstrate the class template vector’s at member function, which performs bounds checking for you. Class template array also has an at member function.

It’s important to ensure that every subscript you use to access an array element is within the array’s bounds—that is, greater than or equal to 0 and less than the number of array elements.

Allowing programs to read from or write to array elements outside the bounds of arrays are common security flaws. Reading from out-of-bounds array elements can cause a program to crash or even appear to execute correctly while using bad data. Writing to an out-of-bounds element (known as a buffer overflow) can corrupt a program’s data in memory, crash a program and allow attackers to exploit the system and execute their own code. For more information on buffer overflows, see http://en.wikipedia.org/wiki/Buffer_overflow.

Common Programming Error 7.4

Referring to an element outside the array bounds is an execution-time logic error, not a syntax error.

 

Error-Prevention Tip 7.1

When looping through an array, the index should never go below 0 and should always be less than the total number of array elements (one less than the size of the array). Make sure that the loop-termination condition prevents accessing elements outside this range. In Section 7.5, you’ll learn about the range-based for statement, which can help prevent accessing elements outside an array’s (or other container’s) bounds.

7.4.8 Static Local arrays and Automatic Local arrays

Chapter 6 discussed the storage-class specifier static. A static local variable in a function definition exists for the program’s duration but is visible only in the function’s body.

Performance Tip 7.1

We can apply static to a local array declaration so that it’s not created and initialized each time the program calls the function and is not destroyed each time the function terminates. This can improve performance, especially when using large arrays.

A program initializes static local arrays when their declarations are first encountered. If a static array is not initialized explicitly by you, each element of that array is initialized to zero by the compiler when the array is created. Recall that C++ does not perform such default initialization for other local variables.

Figure 7.10 demonstrates functions staticArrayInit (lines 23–40) with a static local array (line 25) and automaticArrayInit (lines 43–60) with an automatic local array (line 45)—local variables are sometimes called automatic variables because they’re automatically destroyed when the function finishes executing.

Function staticArrayInit is called twice (lines 13 and 17). The static local array1 is initialized to zero by the compiler the first time the function is called. The function prints the array’s elements, then adds 5 to and prints each element again. The second time the function is called, the static array contains the modified values stored during the first function call. Function automaticArrayInit also is called twice (lines 14 and 18). Automatic local array2’s elements are initialized (line 45) with the values 1, 2 and 3. The function prints the array, adds 5 to each element and prints the array again. The second time the function is called, the array elements are reinitialized to 1, 2 and 3. The array is recreated and reinitialized during each call to automaticArrayInit.

Fig. 7.10 static array initialization and automatic array initialization.

Alternate View

 1   // Fig. 7.10: fig07_10.cpp
 2   // static array initialization and automatic array initialization.
 3   #include <iostream>
 4   #include <array>
 5   using namespace std;
 6
 7   void staticArrayInit(); // function prototype
 8   void automaticArrayInit(); // function prototype
 9   const size_t arraySize{3};
10
11   int main() {
12      cout << "First call to each function:
";
13      staticArrayInit();
14      automaticArrayInit();
15
16      cout << "

Second call to each function:
";
17      staticArrayInit();
18      automaticArrayInit();
19      cout << endl;
20   }
21
22   // function to demonstrate a static local array
23   void staticArrayInit(void) {
24      // initializes elements to 0 first time function is called
25      static array<int, arraySize> array1; // static local array
26
27      cout << "
Values on entering staticArrayInit:
";
28
29      // output contents of array1
30      for (size_t i{0}; i < array1.size(); ++i) {
31         cout << "array1[" << i << "] = " << array1[i] << " ";
32      }
33
34      cout << "
Values on exiting staticArrayInit:
";
35
36      // modify and output contents of array1
37      for (size_t j{0}; j < array1.size(); ++j) {
38         cout << "array1[" << j << "] = " << (array1[j] += 5) << " ";
39      }
40   }
41
42  // function to demonstrate an automatic local array
43  void automaticArrayInit(void) {
44     // initializes elements each time function is called
45     array<int, arraySize> array2{1, 2, 3}; // automatic local array
46
47     cout << "

Values on entering automaticArrayInit:
";
48
49     // output contents of array2
50     for (size_t i{0}; i < array2.size(); ++i) {
51        cout << "array2[" << i << "] = " << array2[i] << " ";
52     }
53
54     cout << "
Values on exiting automaticArrayInit:
";
55
56     // modify and output contents of array2
57     for (size_t j{0}; j < array2.size(); ++j) {
58        cout << "array2[" << j << "] = " << (array2[j] += 5) << " ";
59     }
60   }

First call to each function:

Values on entering staticArrayInit:
array1[0] = 0  array1[1] = 0  array1[2] = 0
Values on exiting staticArrayInit:
array1[0] = 5  array1[1] = 5  array1[2] = 5

Values on entering automaticArrayInit:
array2[0] = 1  array2[1] = 2  array2[2] = 3
Values on exiting automaticArrayInit:
array2[0] = 6  array2[1] = 7  array2[2] = 8

Second call to each function:

Values on entering staticArrayInit:
array1[0] = 5  array1[1] = 5  array1[2] = 5
Values on exiting staticArrayInit:
array1[0] = 10 array1[1] = 10   array1[2] = 10

Values on entering automaticArrayInit:
array2[0] = 1  array2[1] = 2  array2[2] = 3
Values on exiting automaticArrayInit:
array2[0] = 6  array2[1] = 7  array2[2] = 8
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset