The following programs add linear a array to practice problems introduced in the previous chapter. Linear arrays have one dimension, so they look like a list or a single line of elements. The programs demonstrate how to define an array and access its elements with an index (or subscript). They illustrate a series of programs forming a stepwise development of a program that reads a variable number of values from the console and stores them in an array. The series also creates a reusable function that calculates the average (mean) of the values stored in the array. The first two programs fill the array with a fixed number of pseudo-random values1. The third program organizes the demonstration into its final, more authentic form: a client allowing a user to enter an indeterminate number of values - ostensibly assignment scores - through the console, and a supplier function that calculates the average.
Although generating pseudo-random numbers is a distraction from the primary purpose of the examples (demonstrating fundamental array syntax), it allows the initial programs to focus on arrays with little regard to the source of the stored values. The generating process uses object-oriented and operator-overloading features introduced in subsequent chapters. Consequently, the text only briefly describes them. The optional video and footnote provide a general background for the interested reader.
Array Averaging Program 1
#include <iostream>
#include <random> (a)
#include <chrono>
#include <time.h>
using namespace std;
int main()
{
long ticks = chrono::system_clock::now().time_since_epoch().count(); (b)
default_random_engine rng((unsigned)(ticks)); (c)
uniform_int_distribution<int> range(1, 100); (d)
int numbers[10]; (e)
for (int i = 0; i < 10; i++) (f)
numbers[i] = range(rng);
double sum = 0; (g)
for (int i = 0; i < 10; i++) (h)
sum += numbers[i];
cout << "The average is " << sum / 10 << endl; (i)
return 0;
}
average 1: Single function version.
The program defines a 10-element array, fills it with pseudo-random numbers, and calculates their average.
The header files for the pseudo-random number generator (RNG) and the time classes.
chrono::system_clock::now() creates an object that can access the computer's clock. time_since_epoch() returns an object representing the system-dependent time from the epoch until now. count converts the time into an integer suitable for initializing a random-number generator (RNG).
rng is an instance (object) of default_random_engine, a C++ class implementing a pseudo-random number generator.
range is an instance of uniform_int_distribution<int>, a class implementing a uniform distribution. The constructor configures the distribution to generate integers in the range of 1 to 100 (inclusive) with equal probability.
Defines a one-dimensional, 10-element array.
Fills the array with 10 pseudo-random numbers. The assignment operation treats each array element as an l-value (a value that can appear on the left side of the assignment operator). The range object uses rng as a source of pseudo-randomness and restricts the resulting integers to the range [1-100].
Defines and initializes an accumulator variable. Defining the variable as a double prevents a truncation error when the program averages the integers.
Calculates the total of all the numbers. The addition operation treats each array element as an r-value (a value that can appear on the right side of the assignment operator).
Calculates and prints the average.
Array Averaging Program 2
The second version of the program moves the averaging operations - steps (g) and (h) in the previous figure - to an average function, demonstrating passing a one-dimensional array as a function argument. The example illustrates two significant aspects of passing arrays as function arguments:
By default, C++ passes most data types to functions by value. Arrays are the exception to this rule - C++ always passes arrays by-pointer. The example shows two versions of the function prototype and the function header. The different versions are equivalent, and programs only need one of them.
Only the array's name appears in the function call because the name, without the square brackets, is a pointer - the array's address.
#include <iostream>
#include <random>
#include <chrono>
using namespace std;
double average(int* numbers); // (a)
int main()
{
default_random_engine
rng((unsigned)(chrono::system_clock::now().time_since_epoch().count()));
uniform_int_distribution<int> range(1, 100);
int numbers[10];
for (int i = 0; i < 10; i++)
numbers[i] = range(rng);
cout << "The average is " << average(numbers) << endl; // (b)
return 0;
}
double average(int* numbers) // (c)
{
double sum = 0; // (d)
for (int i = 0; i < 10; i++) // (e)
sum += numbers[i];
return sum / 10; // (f)
}
average 2: average function version.
The second program is an intermediate version that transitions between Figures 1 and 3. Although it adds an average function, the function still lacks a significant feature necessary for a useful, general-purpose average function. The random-number generating code is the same as in Figure 1, but it nests some operations.
Illustrates my preferred function prototype, but double average(int numbers[]) is also valid.
Calls the average function, passing in the numbers array by pointer, and printing the returned value.
As with the prototype, programmers may choose the notation denoting a one-dimensional array parameter.
Define and initialize an accumulator variable.
Total the numbers in the array. The function assumes the array's size is 10 - it only works with an array of 10 numbers!
Calculate and return the average.
Array Averaging Program 3
Making the averaging operation a function and generalizing it allows programs to calculate averages wherever a program needs that functionality. However, C++ implements arrays as contiguous blocks of memory and manipulates them as pointers. Arrays are not objects and do not intrinsically maintain their size (the number of filled elements) nor their capacity (total available elements). Consequently, C++ programs must maintain these values as discrete variables. (C++ does have a more-capable array class, but, like the pseudo-random number generator, it relies on concepts introduced later in the text.) The primary generalizing task is to provide the function with the array size, but we also change the function parameter names as described below.
#include <iostream>
using namespace std;
double average(int size, int* a); // (a)
int main()
{
int scores[1000]; // (b)
int count = 0; // (c)
while (count < 1000) // (d)
{
int score;
cout << "Please enter a score: ";
cin >> score;
if (score != -1) // (e)
scores[count++] = score; // (f)
else
break;
}
if (count == 1000) // (g)
cout << "The number of entered scores exceeds 1000" << endl;
cout << "The average is " << average(count, scores) << endl; // (h)
return 0;
}
average 3: Console array input.
The final version of the averaging program allows users to enter any number of non-negative scores. Input ends when the user enters a -1 or the array is full. The program calculates and prints the average of all scores.
Function prototype: size is the number of filled array elements, and a is the array, passed by pointer.
At compile-time, programmers can't know how many scores the user will enter, so the program makes the array big enough to hold many scores - typically more than necessary.
Defines and initializes the accumulator the program uses to count the scores as the user enters them.
The while-loop ends the program when the array is full, preventing a buffer overrun error.
The user ends data input by entering a -1.
Places the entered score into the scores array and counts it.
Prints a diagnostic if the user attempts to enter too many scores.
Calls the average function and prints the returned value. size is the number of array elements that contain data, not the array's capacity. The average function does not require the array's capacity. Note that scores.length does not work in C++.
double average(int size, int* a) // (a)
{
if (size == 0) // (b)
{
cerr << "Array is empty" << endl;
return 0;
}
double sum = 0;
for (int i = 0; i < size; i++)
sum += a[i];
return sum / size; // (c)
}
average 3: General average function.
This average function is sufficiently general to calculate the average of any array of integers.
The program passes the value stored in count to the size parameter, but changing the name recognizes that the functions use the value in different ways. main uses it as an accumulator, counting the scores as the user enters them, while average uses it as the array's size, driving the function's operations. Changing the array's name acknowledges that users may process data other than scores.
In version 2, a for-loop always fills the numbers array with ten pseudo-random numbers. In this version, the user may interrupt the loop before entering a valid score by entering a -1. If this happens, count is not incremented, leaving the size set to 0, and causing a divide-by-zero error at statement (c). The function detects this situation and displays a diagnostic. Throwing an exception (introduced in a subsequent chapter) is an alternate and more appropriate response to returning a 0.
Given the precondition, size > 0, the statement calculates and returns the final average.
Pseudo-random numbers are not random - they arise from deterministic calculations - but look random (i.e., pass some statistical tests of randomness). Pseudo-random number generators produce a long cycle of numbers (i.e., a sequence of numbers that eventually repeats) using a recurrence relation: Rn = f(Rn-1). Programs typically use only a small part of the cycle, which increases the random appearance. Programs start a random number generator at some point in the cycle by specifying a "seed" value: an initial Ri.
Pseudo-random number generators frequently use the computer's clock to get the elapsed time since the epoch. The elapsed time is a monotonically increasing value - it remains unchanged between clock "ticks" but never decreases - making it an effective, low-cost seed value. Some generators can produce various distributions - a value's frequency of occurrence in a given range. The values in a uniform distribution are equally likely.
Please see the optional Random Number Generators video at the top for more detail.