In This Chapter
Considering the need for something like an array
Introducing the array data type
Using an array
Using the most common type of array — the character string
An array is a sequence of variables that shares the same name and that is referenced using an index. Arrays are useful little critters that allow you to store a large number of values of the same type that are related in some way — for example, the batting averages of all the players on the same team might be a good candidate for storage within an array. Arrays can be multidimensional, too, allowing you, for example, to store an array of batting averages within an array of months, which allows you to work with the batting averages of the team as they occur by month.
In this chapter, you find out how to initialize and use arrays for fun and profit. You also find out about an especially useful form of array called a char string.
Consider the following problem. You need a program that can read a sequence of numbers from the keyboard and display their sum. You guessed it — the program stops reading in numbers as soon as you enter a negative number. Unlike similar programs in Chapters 5 and 6, however, this program will output all the numbers entered before displaying the average.
You could try to store numbers in a set of independent variables, as in
cin >> value1; if (value1 >= 0) { cin >> value2; if (value2 >= 0) { ...
You can see that this approach can't handle sequences involving more than just a few numbers. Besides, it's ugly. What we need is some type of structure that has a name like a variable but that can store more than one value. May I present to you, Ms. A. Ray.
An array solves the problem of sequences nicely. For example, the following snippet declares an array valueArray
that has storage for up to 128 int
values. It then populates the array with numbers entered from the keyboard:
int nValue; // declare an array capable of holding up to 128 ints int nValueArray[128]; // define an index used to access subsequent members of // of the array; don't for (int i = 0; i < 128; i++) { cin >> nValue; // exit the loop when the user enters a negative // number if (nValue < 0) { break; } nValueArray[i] = nValue; }
The second line of this snippet declares an array nValueArray
. Array declarations begin with the type of the array members: in this case, int
. This is followed by the name of the array. The last elements of an array declaration are open and closed brackets containing the maximum number of elements that the array can hold. In this code snippet, nValueArray
can store up to 128 integers.
This snippet reads a number from the keyboard and stores it into each subsequent member of the array nValueArray
. You access an individual element of an array by providing the name of the array followed by brackets containing the index. The first integer in the array is nValueArray[0]
, the second is nValueArray[1]
, and so on.
In use, nValueArray[i]
represents the ith element in the array. The index variable i
must be a counting variable — that is, i
must be a char
, an int
, or a long
. If nValueArray
is an array of int
s, nValueArray[i]
is an int
.
The following program inputs a sequence of integer values from the keyboard until the user enters a negative number. The program then displays the numbers input and reports their sum.
// ArrayDemo - demonstrate the use of arrays // by reading a sequence of integers // and then displaying them and their sum #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; // prototype declarations int readArray(int integerArray[], int maxNumElements); int sumArray(int integerArray[], int numElements); void displayArray(int integerArray[], int numElements); int main(int nNumberofArgs, char* pszArgs[]) { // input the loop count cout << "This program sums values entered " << "by the user "; cout << "Terminate the loop by entering " << "a negative number "; cout << endl; // read numbers to be summed from the user into a // local array int inputValues[128]; int numberOfValues = readArray(inputValues, 128); // now output the values and the sum of the values displayArray(inputValues, numberOfValues); cout << "The sum is " << sumArray(inputValues, numberOfValues) << endl; // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; }
// readArray - read integers from the operator into // 'integerArray' // Return the number of elements stored. int readArray(int integerArray[], int maxNumElements) { int numberOfValues; for(numberOfValues = 0; numberOfValues < maxNumElements; numberOfValues++) { // fetch another number int integerValue; cout << "Enter next number: "; cin >> integerValue; // if it's if (integerValue < 0) { // ...then exit break; } // ... otherwise store the number // into the storage array integerArray[numberOfValues] = integerValue; } // return the number of elements read return numberOfValues; } // displayArray - display the members of an // array of length sizeOfloatArray void displayArray(int integerArray[], int numElements) { cout << "The value of the array is:" << endl; for (int i = 0; i < numElements; i++) { cout << i << ": " << integerArray[i] << endl; } cout << endl; } // sumArray - return the sum of the members of an // integer array int sumArray(int integerArray[], int numElements) { int accumulator = 0; for (int i = 0; i < numElements; i++) { accumulator += integerArray[i]; } return accumulator; }
The program ArrayDemo
begins with prototype declarations of the functions readArray(), sumArray()
, and displayArray()
, which it will need later. The main program starts with a prompt to the user to input data to be summed. The program then declares an array inputValues[]
to be used to store the values input by the user. The main program passes this array to readArray()
, along with the length of the array — readArray()
cannot read more than 128 values even if the user does not enter a negative number since that's all the room allocated in the inputValues[]
array.
The array inputValues
is declared as 128 integers long. If you're thinking that this must be more than enough, don't count on it. No matter how large you make the array, always put a check to make sure that you do not exceed the limits of the array. Writing more data than an array can hold causes your program to perform erratically and often to crash.
The main function then calls displayArray()
to print the contents of the array. Finally, the function calls sumArray()
to add the elements in the array.
The readArray()
function takes two arguments: the integerArray[]
into which to store the values it reads and maxNumElements
, the maximum number of integer values for which there is room at the inn. The function begins with a for
loop that reads integer values. Every non-negative value that the function reads is saved into integerArray[]
. The first element goes into integerArray[0]
, the second into integerArray[1]
, and so forth.
Once the user enters a negative number, the program breaks out of the loop and returns the total numberOfValues
input.
The displayArray()
function also uses a for
loop to traverse the elements of the array, starting at 0 and continuing to the last element, which is numElements - 1
. The final function, sumArray()
, also iterates through the array but sums the elements stored there into accumulator
, which it then returns to the caller.
Notice, yet again, that the index i
in the displayArray()
and sumArray()
functions is initialized to 0 and not to 1. In addition, notice how the for
loop terminates as soon as i
reaches numElements
. The output from a sample run appears as follows:
This program sums values entered by the user Terminate the loop by entering a negative number Enter next number: 10 Enter next number: 20 Enter next number: 30 Enter next number: 40 Enter next number: −1 The value of the array is: 0: 10 1: 20
2: 30 3: 40 The sum is 100 Press any key to continue...
Just to keep nonprogrammers guessing, the term iterate means to traverse through a set of objects such as an array. Programmers say that the preceding functions iterate through the array. In a similar fashion, I "get irate" when my dog iterates from one piece of furniture to another.
A local variable does not start life with a valid value, not even the value 0. Said another way, a local variable contains garbage until you actually store something in it. Locally declared arrays are the same — each element contains garbage until you actually assign something to it. You should initialize local variables when you declare them. This rule is even truer for arrays. It is far too easy to access uninitialized array elements thinking that they are valid values.
Fortunately, a small array may be initialized at the time it is declared. The following code snippet demonstrates how this is done:
float floatArray[5] = {0.0, 1.0, 2.0, 3.0, 4.0};
This initializes floatArray[0]
to 0, floatArray[1]
to 1.0, floatArray[2]
to 2.0, and so on.
C++ pads the initialization list with 0s if the number of elements in the list is less than the size of the array. In fact, an empty list can be used to initialize an array to 0:
int nArray[128] = {}; // initialize array to all 0's
The number of initialization constants can determine the size of the array. For example, you could have determined that floatArray
has five elements just by counting the values within the braces. C++ can count as well (here's at least one thing C++ can do for itself).
The following declaration is identical to the preceding one:
float floatArray[] = {0.0, 1.0, 2.0, 3.0, 4.0};
Mathematicians start counting arrays with 1. Most program languages start with an offset of 1 as well. C++ arrays begin counting at 0. The first member of a C++ array is valueArray[0]
. That makes the last element of a 128-integer array integerArray[127]
and not integerArray[128]
.
Unfortunately for the programmer, C++ does not check to see whether the index you are using is within the range of the array. C++ is perfectly happy giving you access to integerArray[200]
. Our integerArray
yard is only 128 integers long — 200 is 72 integers into someone else's yard. No telling who lives there and what he's storing at that location. Reading from integerArray[200]
will return some unknown and unpredictable value. Writing to that location generates unpredictable results. It may do nothing — the house may be abandoned and the yard unused. On the other hand, it might overwrite some data, thereby confusing the neighbor and making the program act in a seemingly random fashion. Or it might crash the program.
The most common wrong way to access an array is to read or write location integerArray[128]
. Although it's only one element beyond the end of the array, reading or writing this location is just as dangerous as using any other incorrect address.
On the surface, the ArrayDemo
program doesn't do anything more than our earlier, non-array-based programs did. True, this version can replay its input by displaying the set of input numbers before calculating their sum, but this feature hardly seems earth shattering.
Yet, the ability to redisplay the input values hints at a significant advantage to using arrays. Arrays allow the program to process a series of numbers multiple times. The main program was able to pass the array of input values to displayArray()
for display and then repass the same numbers to sumArray()
for addition.
Arrays are adept at storing sequences of numbers. Some applications require sequences of sequences. A classic example of this matrix configuration is the spreadsheet. Laid out like a chessboard, each element in the spreadsheet has both an x
and a y
offset.
C++ implements the matrix as follows:
int intMatrix[10][5];
This matrix is 10 elements in 1 dimension, and 5 in another, for a total of 50 elements. In other words, intMatrix
is a 10-element array, each element of which is a 5-int
array. As you might expect, one corner of the matrix is in intMatrix[0][0]
while the other corner is intMatrix[9][4]
.
Whether you consider intMatrix
to be 10 elements long in the x
dimension or in the y
dimension is a matter of taste. A matrix can be initialized in the same way that an array is:
int intMatrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
This line initializes the 3-element array intMatrix[0]
to 1, 2, and 3; and the 3-element array intMatrix[1]
to 4, 5, and 6, respectively.
The elements of an array can be of any type. Arrays of floats, doubles, and longs are all possible; however, arrays of characters have particular significance.
Human words and sentences can be expressed as an array of characters. An array of characters containing my first name would appear as
char sMyName[] = {'S', 't', 'e', 'p', 'h', 'e', 'n'};
The following small program displays my name:
// CharDisplay - output a character array to // standard output, the MS-DOS window #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; // prototype declarations void displayCharArray(char charArray[], int sizeOfArray);
int main(int nNumberofArgs, char* pszArgs[]) { char charMyName[]={'S', 't', 'e', 'p', 'h', 'e', 'n'}; displayCharArray(charMyName, 7); cout << endl; // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; } // displayCharArray - display an array of characters // by outputting one character at // a time void displayCharArray(char charArray[], int sizeOfArray) { for(int i = 0; i< sizeOfArray; i++) { cout << charArray[i]; } }
The program declares a fixed array of characters charMyName
containing — you guessed it — my name (what better name?). This array is passed to the function displayCharArray()
along with its length. The displayCharArray()
function is identical to the displayArray()
function in the earlier example program except that this version displays char
s rather than int
s.
This program works fine; however, it is inconvenient to pass the length of the array with the array itself. If we could come up with a rule for determining the end of the string of characters, we wouldn't need to pass its length — you would know that the string was complete when you encountered the special rule that told you so.
In many cases, all values for each element are possible. However, C++ reserves the special "character" 0 as the noncharacter. We can use ' '
to mark the end of a character array. (The numeric value of ' '
is 0, but the type of ' '
is char
.)
The character 'y'
is the character whose octal value is y. The character ' '
is the character with a value of 0, otherwise known as the null character. Using that rule, the previous small program becomes
// DisplayString - output a character array to // standard output, the MS-DOS window #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; // prototype declarations void displayString(char stringArray[]); int main(int nNumberofArgs, char* pszArgs[]) { char charMyName[] = {'S', 't', 'e', 'p', 'h', 'e', 'n', ' '}; displayString(charMyName); cout << endl; // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; } // displayString - display a character string // one character at a time void displayString(char stringArray[]) { for(int i = 0; stringArray[i] != ' '; i++) { cout << stringArray[i]; } }
The declaration of charMyName
declares the character array with the extra null character ' '
on the end. The displayString
program iterates through the character array until a null character is encountered.
The function displayString()
is simpler to use than its displayCharArray()
predecessor because it is no longer necessary to pass along the length of the character array. This secret handshake of terminating a character array with a null is so convenient that it is used throughout the C++ language. C++ even gives such an array a special name.
A string of characters is a null-terminated character array. It is officially known as a null-terminated byte string, or NTBS. The simpler term C-string is also used to differentiate from the C++ type string
.
The choice of ' '
as the terminating character was not random. Remember that 0 is the only numeric value that converts to false
; all other values translate to true
. This means that the for
loop could (and usually is) written as
for(int i = 0; stringArray[i]; i++)
This whole business of null-terminated character strings is so ingrained in the C++ language psyche that C++ uses a string of characters surrounded by double quotes to be an array of characters automatically terminated with a ' '
character. The following are identical declarations:
char szMyName[] = "Stephen"; char szAlsoMyName[] = {'S', 't', 'e', 'p', 'h', 'e', 'n', ' '};
The naming convention used here is exactly that, a convention. C++ does not care. The prefix sz
stands for zero-terminated string.
The string Stephen
is eight characters long and not seven — the null character after the n
is assumed. The string ""
is one character long, consisting of just the null character.
The following Concatenate
program inputs two strings from the keyboard and concatenates them into a single string:
// Concatenate - concatenate two strings // with a " - " in the middle #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; // prototype declarations void concatString(char szTarget[], const char szSource[]); int main(int nNumberofArgs, char* pszArgs[]) { // read first string... char szString1[260]; cout << "Enter string #1:"; cin.getline(szString1, 128);
// ...now the second string... char szString2[128]; cout << "Enter string #2:"; cin.getline(szString2, 128); // ...concatenate a " - " onto the first... concatString(szString1, " - "); // ...now add the second string... concatString(szString1, szString2); // ...and display the result cout << " " << szString1 << endl; // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; } // concatString - concatenate the szSource string // onto the end of the szTarget string void concatString(char szTarget[], const char szSource[]) { // find the end of the first string int targetIndex = 0; while(szTarget[targetIndex]) { targetIndex++; } // tack the second onto the end of the first int sourceIndex = 0; while(szSource[sourceIndex]) { szTarget[targetIndex] = szSource[sourceIndex]; targetIndex++; sourceIndex++; } // tack on the terminating null szTarget[targetIndex] = ' '; }
The Concatenate
program reads two character strings and appends them together with a " - "
in the middle.
The program begins by reading a string from the keyboard. The program does not use the normal cin >> szString1
for two reasons. First, the cin >>
operation stops reading when any type of whitespace is encountered. Characters up to the first whitespace are read, the whitespace character is tossed, and the remaining characters are left in the input hopper for the next cin >>
statement. Thus, if I were to enter "the Dog", szString2
would be filled with "the" and the word "Dog" would be left in the input buffer.
The second reason is that the getline()
allows the programmer to specify the size of the buffer. The call to getline(szString2, 128)
will not read more than 128 bytes no matter how many are input.
Instead, the call to getline()
inputs an entire line up to but not including the newline at the end. We'll review this function with other file I/O functions in detail in Chapter 23.
After reading the first string into szString1[]
, the program appends " - "
onto the end by calling concatString()
. It concatenates the second string by calling concatString()
with szString2[]
.
The concatString()
function accepts a target string, szTarget
, and a source string, szSource
. The function begins by scanning szTarget
for the terminating null character, which it stores in targetIndex
. The function then enters a second loop in which it copies characters from the szSource
into szTarget
starting at the terminating null. The final statement in concatString()
slaps a terminating null on the completed string.
An example output from the program appears as follows:
Enter string #1:This is a string Enter string #2:THIS IS A STRING This is a string - THIS IS A STRING Press any key to continue...
The C++ programmer is often required to manipulate zero-terminated strings. C++ provides a number of standard string-manipulation functions to make the job easier. A few of these functions are listed in Table 7-1.
Table 7.1. String-Handling Functions
Name | Operation |
---|---|
| Returns the number of characters in a string (not including the terminating null). |
| Copies the source string into a target array. |
| Concatenates the source string onto the end of the target string. |
| Copies a string up to n characters from the source string into a target array. |
| Concatenates the source string onto the end of the target string or n characters, whichever comes first. |
| Returns the address of the first occurrence of pattern in string. Returns a null if pattern is not found. |
| Compares two strings. Returns −1 if source1 occurs before source2 in the dictionary and 1 if later. Returns 0 if the two strings match exactly. |
| #Compares the first n characters in two strings. |
You need to add the statement #include <cstring>
to the beginning of any program that uses a str..
. function because this include file contains the prototype declarations that C++ requires to check up on your work.
The arguments to the str...()
functions appear backward to any reasonable individual (you might consider this an acid test for "reasonable"). For example, the function strcat(target, source)
tacks the second string source
onto the end of the first argument target
.
The strncpy()
and strncat()
functions are similar to their strcpy()
and strcat()
counterparts except that they accept the length of the target buffer as one of their arguments. The call strncpy(szTarget, szSource, 128)
says "copy the characters in szSource
into szTarget
until you copy a null character or until you've copied 128 characters, whichever comes first." This avoids inadvertently writing beyond the end of the source string array.
The standard C++ library includes similar functions to handle wide character strings. A few of these functions are listed in Table 7-2.
Remember from Chapter 2 that wide characters are used for applications that must support foreign languages, where a measly 255 different characters may not be enough.
Table 7.2. Wide String-Handling Functions
Name | Operation |
---|---|
| Returns the number of wide characters in a string not including the terminating null. |
| Copies the source wide string into a target array. |
| Concatenates the source wide string onto the end of the target wide string. |
| Copies a wide string up to n characters from the source string into a target array. |
| Concatenates the source string onto the end of the target string or n characters, whichever comes first. |
| Finds the address of the first occurrence of pattern in string. Returns a null if pattern is not found. |
| Compares two wide strings. Returns −1 if source1 occurs before source2 in the dictionary and 1 if later. Returns 0 if the two strings match exactly. |
| #Compares the first n wide characters in two wide strings. |
The following shows a wide character version of the Concatenate
program:
// ConcatenateWide - concatenate two wide strings // with a " - " in the middle using library routines #include <cstdio> #include <cstdlib> #include <iostream> using namespace std;
int main(int nNumberofArgs, char* pszArgs[]) { // read first string... wchar_t wszString1[260]; cout << "Enter string #1:"; wcin.getline(wszString1, 128); // ...now the second string... wchar_t wszString2[128]; cout << "Enter string #2:"; wcin.getline(wszString2, 128); // now tack the second onto the end of the first // with a dash in between wcsncat(wszString1, L" - ", 260); wcsncat(wszString1, wszString2, 260); wcout << L" " << wszString1 << endl; // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; }
The wide character string program looks similar to its single-byte character string cousin except for the following differences:
Variables are declared wchar_t
rather than char
.
Constant characters and constant strings appear preceded by an L
, as in L"This is a wide string"
.
The objects wcin
and wcout
are used in place of cin
and cout
for input and output.
The wcs...
functions appear in place of the narrow str...
functions.
The output from ConcatenateWide
appears identical to that of the char
based Concatenate
program to those of us who do most of their input/output in European languages. The topic of writing programs capable of handling multiple languages with different alphabets and rules of grammar is known as localization and beyond a beginning book.
ANSI C++ includes a type string
designed to make it easier to manipulate strings of text. However, this type makes use of features of the language that you haven't seen yet. I return to the string
type in Chapter 13.
18.118.32.222