Arrays and Strings
The Basic Idea and Notation
Although we have already seen how to store large amounts of data in files, we have as yet no convenient way to manipulate such data from within programs. For example, we might want to write a program that inputs and then ranks or sorts a long list of numbers. C++ provides a structured data type called an array to facilitate this kind of task. The use of arrays permits us to set aside a group of memory locations (i.e. a group of variables) that we can then manipulate as a single entity, but that at the same time gives us direct access to any individual component. Arrays are simple examples of structured data types - they are effectively just lists of variables all of the same data type ("int", "char" or whatever). Later in the course you will learn how to construct more complicated compound data structures.
Declaring an array
The general syntax for an array declaration is:
<component type> <variable identifier>[<integer value>];
For example, suppose we are writing a program to manipulate data concerning the number of hours a group of 6 employees have worked in a particular week. We might start the program with the array declaration:
int hours[6];
or better,
const int NO_OF_EMPLOYEES = 6;
int hours[NO_OF_EMPLOYEES];
Indeed, if we are going to use a number of such arrays in our program, we can even use a type definition:
const int NO_OF_EMPLOYEES = 6;
typedef int Hours_array[NO_OF_EMPLOYEES];
Hours_array hours;
Hours_array hours_week_two;
In each case, we end up with 6 variables of type "int" with identifiers
hours[0] hours[1] hours[2] hours[3] hours[4] hours[5]
Each of these is referred to as an element or component of the array. The numbers 0, ..., 5 are the indexes or subscripts of the components. An important feature of these 6 variables is that they are allocated consecutive memory locations in the computer.
Assignment Statements and Expressions with Array Elements
Having declared our array, we can treat the individual elements just like ordinary variables (of type "int" in the particular example above). In particular, we can write assignment statements such as
hours[4] = 34;
hours[5] = hours[4]/2;
and use them in logical expressions, e.g.
if (number < 4 && hours[number] >= 40) { ...
A common way to assign values to an array is using a "for" or "while" loop. The following program prompts the user for the number of hours that each employee has worked. It is more natural to number employees from 1 to 6 than from 0 to 5, but it is important to remember that array indexes always start from 0. Hence the program subtracts 1 from each employee number to obtain the corresponding array index.
#include <iostream>
using namespace std;
const int NO_OF_EMPLOYEES = 6;
typedef int Hours_array[NO_OF_EMPLOYEES];
int main()
{
Hours_array hours;
int count;
for (count = 1 ; count <= NO_OF_EMPLOYEES ; count++)
{
cout << "Enter hours for employee number " << count << ": ";
cin >> hours[count - 1];
}
return 0;
}
Program 6.1.1
A typical run might produce the following input/output:
Enter hours for employee number 1: 38
Enter hours for employee number 2: 42
Enter hours for employee number 3: 29
Enter hours for employee number 4: 35
Enter hours for employee number 5: 38
Enter hours for employee number 6: 37
n other words, C++ would have simply put the value "37" into the next integer-sized chunk of memory located after the memory block set aside for the array "hours". This is a very undesirable situation - the compiler might have already reserved this chunk of memory for another variable (perhaps, for example, for the variable "count").
Array elements can be of data types other than "int". Here's a program that prints itself out backwards on the screen, using an array of type "char".
#include <iostream>
#include <fstream>
using namespace std;
const int MAX = 1000;
typedef char File_array[MAX];
int main()
{
char character;
File_array file;
int count;
ifstream in_stream;
in_stream.open("6-1-2.cpp");
in_stream.get(character);
for (count = 0 ; ! in_stream.eof() && count < MAX ; count++)
{
file[count] = character;
in_stream.get(character);
}
in_stream.close();
while (count > 0)
cout << file[--count];
return 0;
}
Program 6.1.2
Note the use of the condition "... && count < MAX ; ..." in the head of the "for" loop, to avoid the possibility of a range bound error.
Arrays as Parameters in Functions
Functions can be used with array parameters to maintain a structured design. Here is a definition of an example function which returns the average hours worked, given an array of type "Hours_array" from Program 6.1.1
float average(Hours_array hrs)
{
float total = 0;
int count;
for (count = 0 ; count < NO_OF_EMPLOYEES ; count++)
total += float(hrs[count]);
return (total / NO_OF_EMPLOYEES);
}
Fragment of Program 6.2.1
We could make this function more general by including a second parameter for the length of the array:
float average(int list[], int length)
{
float total = 0;
int count;
for (count = 0 ; count < length ; count++)
total += float(list[count]);
return (total / length);
}
It is quite common to pass the array length to a function along with an array parameter, since the syntax for an array parameter (such as "int list[]" above) doesn't include the array's length.
Although array parameters are not declared with an "&" character in function declarations and definitions, they are effectively reference parameters (rather than value parameters). In other words, when they execute, functions do not make private copies of the arrays they are passed (this would potentially be very expensive in terms of memory). Hence, like the reference parameters we have seen in Lecture 3, arrays can be permanently changed when passed as arguments to functions. For example, after a call to the following function, each element in the third array argument is equal to the sum of the corresponding two elements in the first and second arguments:
void add_lists(int first[], int second[], int total[], int length)
{
int count;
for (count = 0 ; count < length ; count++)
total[count] = first[count] + second[count];
}
Fragment of Program 6.2.2
As a safety measure, we can add the modifier "const" in the function head:
void add_lists(const int fst[], const int snd[], int tot[], int len)
{
int count;
for (count = 0 ; count < len; count++)
tot[count] = fst[count] + snd[count];
}
The compiler will then not accept any statements within the function's definition which potentially modify the elements of the arrays "fst" or "snd". Indeed, the restriction imposed by the "const" modifier when used in this context is stronger than really needed in some situations. For example, the following two function definitions will not be accepted by the compiler:
void no_effect(const int list[])
{
do_nothing(list);
}
void do_nothing(int list[])
{
;
}
Fragment of Program 6.2.3
This is because, although we can see that "do_nothing(...)" does nothing, its head doesn't include the modifier "const", and the compiler only looks at the head of "do_nothing(...)" when checking to see if the call to this function from within "no_effect(...)" is legal.
|