|

You
access each of the array elements by referring to
an offset from the array name. Array elements are
counted from zero. Therefore, the first array element
is arrayName[0]. In the LongArray example, LongArray[0]
is the first array element, LongArray[1] the second,
and so forth.
This
can be somewhat confusing. The array SomeArray[3]
has three elements. They are SomeArray[0], SomeArray[1],
and SomeArray[2]. More generally, SomeArray[n] has
n elements that are numbered SomeArray[0] through
SomeArray[n-1].
Therefore,
LongArray[25] is numbered from LongArray[0] through
LongArray[24]. Listing 11.1 shows how to declare
an array of five integers and fill each with a value.
Listing
11.1. Using an integer array.
1:
//Listing 11.1 - Arrays
2:
#include <iostream.h>
3:
4:
int main()
5:
{
6:
int myArray[5];
7:
int i;
8:
for ( i=0; i<5; i++) // 0-4
9:
{
10:
cout << "Value for myArray[" << i <<
"]: ";
11:
cin >> myArray[i];
12:
}
13:
for (i = 0; i<5; i++)
14:
cout << i << ": " << myArray[i]
<< "\n";
15:
return 0;
16:
}
Output:
Value for myArray[0]: 3
Value for myArray[1]: 6
Value for myArray[2]: 9
Value for myArray[3]: 12
Value for myArray[4]: 15
0: 3
1: 6
2: 9
3: 12
4: 15
ANALYSIS: Line 6 declares an array
called myArray, which holds five integer variables.
Line 8 establishes a loop that counts from 0 through
4, which is the proper set of offsets for a five-element
array. The user is prompted for a value, and that
value is saved at the correct offset into the array.
The
first value is saved at myArray[0], the second at
myArray[1], and so forth. The second for loop prints
each value to the screen.
NOTE: Arrays count from 0, not from
1. This is the cause of many bugs in programs written
by C++ novices. Whenever you use an array, remember
that an array with 10 elements counts from ArrayName[0]
to ArrayName[9]. There is no ArrayName[10].
Writing
Past the End of an Array
When
you write a value to an element in an array, the
compiler computes where to store the value based
on the size of each element and the subscript. Suppose
that you ask to write over the value at LongArray[5],
which is the sixth element. The compiler multiplies
the offset (5) by the size of each element—in this
case, 4. It then moves that many bytes (20) from
the beginning of the array and writes the new value
at that location.
If
you ask to write at LongArray[50], the compiler
ignores the fact that there is no such element.
It computes how far past the first element it should
look (200 bytes) and then writes over whatever is
at that location. This can be virtually any data,
and writing your new value there might have unpredictable
results. If you’re lucky, your program will crash
immediately. If you’re unlucky, you’ll get strange
results much later in your program, and you’ll have
a difficult time figuring out what went wrong.
The
compiler is like a blind man pacing off the distance
from a house. He starts out at the first house,
MainStreet[0]. When you ask him to go to the sixth
house on Main Street, he says to himself, "I must
go five more houses. Each house is four big paces.
I must go an additional 20 steps." If you ask him
to go to MainStreet[100], and Main Street is only
25 houses long, he will pace off 400 steps. Long
before he gets there, he will, no doubt, step in
front of a moving bus. So be careful where you send
him.
Listing
11.2 shows what happens when you write past the
end of an array.
WARNING: Do not run this program; it
may crash your system!
Listing
11.2. Writing past the end of an array.
1:
//Lis1
22:
23:
for (i = 0; i<3; i++)
24:
{
25:
cout << "sentinelOne[" << i <<
"]: ";
26:
cout << sentinelOne[i] << "\n";
27:
cout << "sentinelTwo[" << i <<
"]: ";
28:
cout << sentinelTwo[i]<< "\n";
29:
}
30:
31:
cout << "\nAssigning...";
32:
for (i = 0; i<=25; i++)
33:
TargetArray[i] = 20;
34:
35:
cout << "\nTest 2: \n";
36:
cout << "TargetArray[0]: " << TargetArray[0]
<< "\n";
37:
cout << "TargetArray[24]: " << TargetArray[24]
<< "\n";
38:
cout << "TargetArray[25]: " << TargetArray[25]
<< "\n\n";
39:
for (i = 0; i<3; i++)
40:
{
41:
cout << "sentinelOne[" << i <<
"]: ";
42:
cout << sentinelOne[i]<< "\n";
43:
cout << "sentinelTwo[" << i <<
"]: ";
44:
cout << sentinelTwo[i]<< "\n";
45:
}
46:
47:
return 0;
48:
}
OUTPUT:
Test 1:
TargetArray[0]: 0
TargetArray[24]: 0
SentinelOne[0]: 0
SentinelTwo[0]: 0
SentinelOne[1]: 0
SentinelTwo[1]: 0
SentinelOne[2]: 0
SentinelTwo[2]: 0
Assigning...
Test 2:
TargetArray[0]: 20
TargetArray[24]: 20
TargetArray[25]: 20
SentinelOne[0]: 20
SentinelTwo[0]: 0
SentinelOne[1]: 0
SentinelTwo[1]: 0
SentinelOne[2]: 0
SentinelTwo[2]: 0
ANALYSIS: Lines 9 and 10 declare two
arrays of three integers that act as sentinels around
TargetArray. These sentinel arrays are initialized
with the value 0. If memory is written to beyond
the end of TargetArray, the sentinels are likely
to be changed. Some compilers count down in memory;
others count up. For this reason, the sentinels
are placed on both sides of TargetArray.
Lines 19-29 confirm the sentinel values in Test
1. In line 33 TargetArray’s members are all initialized
to the value 20, but the counter counts to TargetArray
offset 25, which doesn’t exist in TargetArray.
Lines
36-38 print TargetArray’s values in Test 2. Note
that TargetArray[25] is perfectly happy to print
the value 20. However, when SentinelOne and SentinelTwo
are printed, SentinelTwo[0] reveals that its value
has changed. This is because the memory that is
25 elements after TargetArray[0] is the same memory
that is at SentinelTwo[0]. When the nonexistent
TargetArray[0] was accessed, what was actually accessed
was SentinelTwo[0].
This
nasty bug can be very hard to find, because SentinelTwo[0]’s
value was changed in a part of the code that was
not writing to SentinelTwo at all.
This
code uses "magic numbers" such as 3 for the size
of the sentinel arrays and 25 for the size of TargetArray.
It is safer to use constants, so that you can change
all these values in one place.
Fence
Post Errors
It
is so common to write to one past the end of an
array that this bug has its own name. It is called
a fence post error. This refers to the problem in
counting how many fence posts you need for a 10-foot
fence if you need one post for every foot. Most
people answer 10, but of course you need 11. Figure
11.2 makes this clear.

Figure 11.2. Fence post errors.
This
sort of "off by one" counting can be the bane of
any programmer’s life. Over time, however, you’ll
get used to the idea that a 25-element array counts
only to element 24, and that everything counts from
0. (Programmers are often confused why office buildings
don’t have a floor zero. Indeed, some have been
known to push the 4 elevator button when they want
to get to the fifth floor.)
NOTE: Some programmers refer to ArrayName[0]
as the zeroth element. Getting into this habit is
a big mistake. If ArrayName[0] is the zeroth element,
what is ArrayName[1]? The oneth? If so, when you
see ArrayName[24], will you realize that it is not
the 24th element, but rather the 25th? It is far
better to say that ArrayName[0] is at offset zero
and is the first element.
|