Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Visual C++ 2005 (2006) [eng]-1

.pdf
Скачиваний:
94
Добавлен:
16.08.2013
Размер:
18.66 Mб
Скачать

4

Arrays, Strings, and Pointers

So far, you have covered all the fundamental data types of consequence and you have a basic knowledge of how to perform calculations and make decisions in a program. This chapter is about broadening the application of the basic programming techniques that you have learned so far, from using single items of data to working with whole collections of data items. In this chapter, you will learn about:

Arrays and how you use them

How to declare and initialize arrays of different types

How to declare and use multidimensional arrays

Pointers is and how you use them

How to declare and initialize pointers of different types

The relationship between arrays and pointers

References, how they are declared, and some initial ideas on their uses

How to allocate memory for variables dynamically in a native C++ program

How dynamic memory allocation works in a CLR program

Tracking handles and tracking references and why you need them in a CLR program

How to work with strings and arrays in C++/CLI programs

What interior pointers are and how you can create and use them

In this chapter you’ll be using objects more extensively although you have not yet explored the details of how they are created so don’t worry if everything is not completely clear. You’ll learn about classes and objects in detail starting in Chapter 7.

Chapter 4

Handling Multiple Data Values of the

Same Type

You already know how to declare and initialize variables of various types that each holds a single item of information; I’ll refer to single items of data as data elements. You know how to create a single character in a variable of type char, a single integer in a variable of type short, type int, type long, or a single floating point number in a variable of type float or of type double. The most obvious extension to these ideas is to be able to reference several data elements of a particular type with a single variable name. This would enable you to handle applications of a much broader scope.

Here’s an example of where you might need this. Suppose that you needed to write a payroll program. Using a separately named variable for each individual’s pay, their tax liability, and so on, would be an uphill task to say the least. A much more convenient way to handle such a problem would be to reference an employee by some kind of generic name — employeeName to take an imaginative example — and to have other generic names for the kinds of data related to each employee, such as pay, tax, and so on. Of course, you would also need some means of picking out a particular employee from the whole bunch, together with the data from the generic variables associated with them. This kind of requirement arises with any collection of like entities that you want to handle in your program, whether they’re baseball players or battleships. Naturally, C++ provides you with a way to deal with this.

Arrays

The basis for the solution to all of these problems is provided by the array in ISO/ANSI C++. An array is simply a number of memory locations called array elements or simply elements, each of which can store an item of data of the same given data type and which are all referenced through the same variable name. The employee names in a payroll program could be stored in one array, the pay for each employee in another, and the tax due for each employee could be stored in a third array.

Individual items in an array are specified by an index value which is simply an integer representing the sequence number of the elements in the array, the first having the sequence number 0, the second 1, and so on. You can also envisage the index value of an array element as being an offset from the first element in an array. The first element has an offset of 0 and therefore an index of 0, and an index value of 3

will refer to the fourth element of an array. For the payroll, you could arrange the arrays so that if an employee’s name was stored in the employeeName array at a given index value, then the arrays pay and tax would store the associated data on pay and tax for the same employee in the array positions referenced by the same index value.

The basic structure of an array is illustrated in Figure 4-1.

Figure 4-1 shows an array. The name height has six elements, each storing a different value. These might be the heights of the members of a family, for instance, recorded to the nearest inch. Because there are six elements, the index values run from 0 through 5. To refer to a particular element, you write the array name, followed by the index value of the particular element between square brackets. The third element is referred to as height[2], for example. If you think of the index as being the offset from the first element, it’s easy to see that the index value for the fourth element will be 3, for example.

The amount of memory required to store each element is determined by its type, and all the elements of an array are stored in a contiguous block of memory.

160

 

 

 

 

Arrays, Strings, and Pointers

Index value

 

 

 

Index value

 

 

 

for the 2nd element

for the 5th element

 

Array name

 

 

Array name

 

 

 

 

 

 

 

 

 

 

 

 

 

height[0]

height[1]

height[2]

height[3]

height[4]

height[5]

73

62

51

42

41

34

 

 

 

 

 

 

The height array has 6 elements.

Figure 4-1

Declaring Arrays

You declare an array in essentially the same way as you declared the variables that you have seen up to now, the only difference being that the number of elements in the array is specified between square brackets immediately following the array name. For example, you could declare the integer array height, shown in the previous figure, with the following declaration statement:

long height[6];

Because each long value occupies 4 bytes in memory, the whole array requires 24 bytes. Arrays can be of any size, subject to the constraints imposed by the amount of memory in the computer on which your program is running.

You can declare arrays to be of any type. For example, to declare arrays intended to store the capacity and power output of a series of engines, you could write the following:

double

cubic_inches[10];

//

Engine

size

double

horsepower[10];

//

Engine

power output

If auto mechanics are your thing, this would enable you to store the cubic capacity and power output of up to 10 engines, referenced by index values from 0 to 9. As you have seen before with other variables, you can declare multiple arrays of a given type in a single statement, but in practice it is almost always better to declare variables in separate statements.

Try It Out

Using Arrays

As a basis for an exercise in using arrays, imagine that you have kept a record of both the amount of gasoline you have bought for the car and the odometer reading on each occasion. You can write a program to analyze this data to see how the gas consumption looks on each occasion that you bought gas:

//Ex4_01.cpp

//Calculating gas mileage #include <iostream>

161

Chapter 4

#include <iomanip>

using

std::cin;

 

using

std::cout;

 

using

std::endl;

 

using std::setw;

 

int main()

 

{

 

 

const int MAX = 20;

// Maximum number of values

double gas[ MAX ];

// Gas quantity in gallons

long miles[ MAX ];

// Odometer readings

int count = 0;

// Loop counter

char indicator = ‘y’;

// Input indicator

while( (indicator == ‘y’ || indicator == ‘Y’) && count < MAX )

{

 

 

 

cout << endl

 

 

<< “Enter gas quantity: “;

 

 

cin >> gas[count];

// Read gas quantity

 

cout << “Enter odometer reading: “;

 

cin >> miles[count];

// Read odometer value

 

++count;

 

 

cout << “Do you want to enter another(y or n)? “;

 

cin >> indicator;

 

}

 

 

if(count <= 1)

// count = 1 after 1 entry completed

{

 

// ... we need at least 2

 

cout << endl

 

 

<< “Sorry - at least two readings are necessary.”;

 

return 0;

 

}

 

 

// Output results from 2nd entry to last entry

for(int i = 1; i < count; i++)

 

cout << endl

 

 

<< setw(2) << i << “.”

// Output sequence number

 

<< “Gas purchased = “ << gas[i] << “ gallons” // Output gas

 

<< “ resulted in “

// Output miles per gallon

 

<< (miles[i] - miles[i - 1])/gas[i] << “ miles per gallon.”;

cout << endl;

 

return 0;

 

}

 

 

The program assumes that you fill the tank each time so the gas bought was the amount consumed by driving the distance recorded. Here’s an example of the output produced by this example:

Enter gas quantity: 12.8

Enter odometer reading: 25832

Do you want to enter another(y or n)? y

Enter gas quantity: 14.9

162

Arrays, Strings, and Pointers

Enter odometer reading: 26337

Do you want to enter another(y or n)? y

Enter gas quantity: 11.8

Enter odometer reading: 26598

Do you want to enter another(y or n)? n

1.Gas purchased = 14.9 gallons resulted in 33.8926 miles per gallon. 2.Gas purchased = 11.8 gallons resulted in 22.1186 miles per gallon.

How It Works

Because you need to take the difference between two odometer readings to calculate the miles covered for the gas used, you use the odometer reading only from the first pair of input values — you ignore the gas bought in the first instance as that would have been consumed during miles driven earlier.

During the second period shown in the output, the traffic must have been really bad — or maybe the parking brake was always on.

The dimensions of the two arrays gas and miles used to store the input data are determined by the value of the constant with the name MAX. By changing the value of MAX, you can change the program to accommodate a different maximum number of input values. This technique is commonly used to make a program flexible in the amount of information that it can handle. Of course, all the program code must be written to take account of the array dimensions, or of any other parameters being specified by const variables. This presents little difficulty in practice, however, so there’s no reason why you should not adopt this approach. You’ll also see later how to allocate memory for storing data as the program executes, so that you don’t need to fix the amount of memory allocated for data storage in advance.

Entering the Data

The data values are read in the while loop. Because the loop variable count can run from 0 to MAX - 1, we haven’t allowed the user of our program to enter more values than the array can handle. You initialize the variables count and indicator to 0 and ‘y’ respectively, so that the while loop is entered at least once. There’s a prompt for each input value required and the value is read into the appropriate array element. The element used to store a particular value is determined by the variable count, which is 0 for the first input. The array element is specified in the cin statement by using count as an index, and count is then incremented ready for the next value.

After you enter each value, the program prompts for confirmation that another value is to be entered. The character entered is read into the variable indicator and then tested in the loop condition. The loop will terminate unless ‘y’ or ‘Y’ is entered and the variable count is less than the specified maximum value, MAX.

After the input loop ends (by whatever means), the value of count contains one more than the index value of the last element entered in each array. (Remember, you increment it after you enter each new element.) This is checked in order to verify that at least two pairs of values were entered. If this wasn’t the case, the program ends with a suitable message because two odometer values are necessary to calculate a mileage value.

163

Chapter 4

Producing the Results

The output is generated in the for loop. The control variable i runs from 1 to count-1, allowing mileage to be calculated as the difference between the current element, miles[i] and the previous element, miles[i - 1]. Note that an index value can be any expression evaluating to an integer that represents a legal index for the array in question, which is an index value from 0 to one less than the number of elements in the array.

If the value of an index expression lies outside of the range corresponding to legitimate array elements, you will reference a spurious data location that may contain other data, garbage, or even program code. If the reference to such an element appears in an expression, you will use some arbitrary data value in the calculation, which certainly produces a result that you did not intend. If you are storing a result in an array element using an illegal index value, you will overwrite whatever happens to be in that location. When this is part of your program code, the results are catastrophic. If you use illegal index values, there are no warnings produced either by the compiler or at run-time. The only way to guard against this is to code your program to prevent it happening.

The output is generated by a single cout statement for all values entered, except for the first. A line number is also generated for each line of output using the loop control variable i. The miles per gallon are calculated directly in the output statement. You can use array elements in exactly the same way as any other variables in an expression.

Initializing Arrays

To initialize an array in its declaration, you put the initializing values separated by commas between braces, and you place the set of initial values following an equals sign after the array name. Here’s an example of how you can declare and initialize an array:

int cubic_inches[5] = { 200, 250, 300, 350, 400 };

The array has the name cubic_inches and has five elements that each store a value of type int. The values in the initializing list between the braces correspond to successive index values of the array, so in this case cubic_inches[0] has the value 200, cubic_inches[1] the value 250, cubic_inches[2] the value 300, and so on.

You must not specify more initializing values than there are elements in the array, but you can include fewer. If there are fewer, the values are assigned to successive elements, starting with the first element which is the one corresponding to the index value 0. The array elements for which you didn’t provide an initial value is initialized with zero. This isn’t the same as supplying no initializing list. Without an initializing list, the array elements contain junk values. Also, if you include an initializing list, there must be at least one initializing value in it; otherwise the compiler generates an error message. I can illustrate this with the following rather limited example.

Try It Out

Initializing an Array

//Ex4_02.cpp

//Demonstrating array initialization #include <iostream>

#include <iomanip>

using std::cout;

164

Arrays, Strings, and Pointers

using std::endl; using std::setw;

int main()

{

int value[5] = { 1, 2, 3 }; int junk [5];

cout << endl;

for(int i = 0; i < 5; i++)

cout << setw(12) << value[i];

cout << endl;

for(int i = 0; i < 5; i++) cout << setw(12) << junk[i];

cout << endl; return 0;

}

In this example, you declare two arrays, the first of which, value, you initialize in part, and the second, junk, you don’t initialize at all. The program generates two lines of output, which on my computer look like this:

1

2

3

0

0

-858993460

-858993460

-858993460

-858993460

-858993460

The second line (corresponding to values of junk[0] to junk[4]) may well be different on your computer.

How It Works

The first three values of the array value are the initializing values and the last two have the default value of 0. In the case of junk, all the values are spurious because you didn’t provide any initial values at all. The array elements contain whatever values were left there by the program that last used these memory locations.

A convenient way to initialize a whole array to zero is simply to specify a single initializing value as 0. For example:

long data[100] = {0};

// Initialize all elements to zero

This statement declares the array data, with all one hundred elements initialized with 0. The first element is initialized by the value you have between the braces and the remaining elements are initialized to zero because you omitted values for these.

You can also omit the dimension of an array of numeric type, providing you supply initializing values. The number of elements in the array is determined by the number of initializing values you specify. For example, the array declaration

int value[] = { 2, 3, 4 };

defines an array with three elements that have the initial values 2, 3, and 4.

165

Chapter 4

Character Arrays and String Handling

An array of type char is called a character array and is generally used to store a character string. A character string is a sequence of characters with a special character appended to indicate the end of the string. The string terminating character indicates the end of the string and this character is defined by the escape sequence ‘\0’, and is sometimes referred to as a null character, being a byte with all bits as zero. A string of this form is often referred to as a C-style string because defining a string in this way was introduced in the C language from which C++ was developed by Bjarne Stroustrup. This is not the only representation of a string that you can use you’ll meet others later in the book. In particular, C++/CLI programs use a different representation of a string and the MFC defines a CString class to represent strings.

The representation of a C-style string in memory is shown in Figure 4-2.

 

name[4]

 

 

 

 

 

 

String termination

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

character

 

Each character in a string occupies

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A

l

b

e

r

t

 

E

i

n

s

t

e

i

n

\0

one byte

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

char name[] = “Albert Einstein”;

Figure 4-2

Figure 4-2 illustrates how a string looks in memory and shows a form of declaration for a string that I’ll get to in a moment.

Each character in the string occupies one byte, so together with the terminating null character, a string requires a number of bytes that is one greater than the number of characters contained in the string.

You can declare a character array and initialize it with a string literal. For example:

char movie_star[15] = “Marilyn Monroe”;

Note that the terminating ‘\0’is supplied automatically by the compiler. If you include one explicitly in the string literal, you end up with two of them. You must, however, allow for the terminating null in the number of elements that you allot to the array.

You can let the compiler work out the length of an initialized array for you, as you saw in Figure 4-1. Here’s another example:

char president[] = “Ulysses Grant”;

Because the dimension is unspecified, the compiler allocates space for enough elements to hold the initializing string, plus the terminating null character. In this case it allocates 14 elements for the array president. Of course, if you want to use this array later for storing a different string, its length (including the terminating null character) must not exceed 14 bytes. In general, it is your responsibility to ensure that the array is large enough for any string you might subsequently want to store.

166

Arrays, Strings, and Pointers

String Input

The <iostream> header file contains definitions of a number of functions for reading characters from the keyboard. The one that you’ll look at here is the function getline(), which reads a sequence of characters entered through the keyboard and stores it in a character array as a string terminated by \0. You typically use the getline() function statements like this:

const int MAX = 80;

// Maximum string length including \0

char name[MAX];

// Array to store a string

cin.getline(name, MAX, ‘\n’);

// Read input line as a string

 

 

These statements first declare a char array name with MAX elements and then read characters from cin using the function getline(). The source of the data, cin, is written as shown, with a period separating it from the function name. The significance of the arguments to the getline() function is shown in Figure 4-3.

The maximum number of characters to be read. When the specified maximum has been read, input stops.

The name of the array of type char[] in which the characters read from cin are to be stored.

The character that is to stop the input process. You can specify any character here, and the first occurance of that character will stop the input process.

cin.getline( name , MAX, ‘\n’ );

Figure 4-3

Because the last argument to the getline() function is ‘\n’(newline or end line character) and the second argument is MAX, characters are read from cin until the ‘\n’ character is read, or when MAX - 1 characters have been read, whichever occurs first. The maximum number of characters read is MAX - 1 rather than MAX to allow for the ‘\0’ character to be appended to the sequence of characters stored in the array. The ‘\n’ character is generated when you press the Return key on your keyboard and is therefore usually the most convenient character to end input. You can, however, specify something else by changing the last argument. The ‘\n’ isn’t stored in the input array name, but as I said, a ‘\0’ is added at the end of the input string in the array.

You will learn more about this form of syntax when we discuss classes later on. Meanwhile, we’ll just take it for granted and use it in an example.

Try It Out

Programming With Strings

You now have enough knowledge to write a simple program to read a string and then count how many characters it contains.

//Ex4_03.cpp

//Counting string characters #include <iostream>

using std::cin;

167

Chapter 4

using std::cout; using std::endl;

int main()

 

{

 

const int MAX = 80;

// Maximum array dimension

char buffer[MAX];

// Input buffer

int count = 0;

// Character count

cout << “Enter a string of less than 80 characters:\n”;

cin.getline(buffer, MAX, ‘\n’);

// Read a string until \n

while(buffer[count] != ‘\0’)

// Increment count as long as

count++;

// the current character is not null

cout << endl

<<“The string \”” << buffer

<<“\” has “ << count << “ characters.”;

cout << endl; return 0;

}

Typical output from this program is as follows:

Enter a string of less than 80 characters:

Radiation fades your genes

The string “Radiation fades your genes” has 26 characters.

How It Works

This program declares a character array buffer and reads a character string into the array from the keyboard after displaying a prompt for the input. Reading from the keyboard ends when the user presses Return, or when MAX-1 characters have been read.

A while loop is used to count the number of characters read. The loop continues as long as the current character referenced with buffer[count] is not ‘\0’. This sort of checking on the current character while stepping through an array is a common technique in native C++. The only action in the loop is to increment count for each non-null character.

There is also a library function, strlen(), that can save you the trouble of coding it yourself. If you use it, you need to include the<cstring> header file in your program with an #include directive like this:

#include <cstring>

The ‘c’ in the header file name indicates that this file contains definitions that belong to the C language library that forms part of the C++ standard library. This header also contains the wcsnlen() function that will return the length of a wide character string.

By using the strlen() function you could replace the while loop with the following statement:

count = std::strlen(buffer);

168