Array Indexes Out of Range

   


The minimum valid index value when referencing an array element is 0, as in accountBalances[0] of Listing 10.2. The maximum value is the array length minus 1, as in accountBalances[4]. A common mistake is to write an expression representing an index value, which is evaluated to be either smaller than zero or larger than the maximum valid index, causing the index to be out of range.

When an index, provided to access an array element, is either smaller than the valid minimum index value, or larger than the maximum valid index, this index is said to be out of range.

An out of range index will trigger the .NET runtime to throw an exception (see the following Note) during program execution, not surprisingly called an IndexOutOfRangeException.

Exceptions in C#

graphics/common.gif

When the runtime executes a program, it might, in exceptional cases, encounter unusual or erroneous situations in which case it will generate an exception. When this happens, the runtime is said to throw an exception. An exception will, if not dealt with suitably, cause the program to terminate abruptly. Chapter 19, "Exception Handling," discusses exception handling at length.


Some of the most common causes for out of range exceptions are found when combining loop constructs with arrays. I have provided two typical scenarios.

1) An incorrect loop condition that causes the loop body to be executed one time too many.

Consider the following for loop:


graphics/10infig04.gif

The less-than-or-equal-to comparison operator used in the for loop condition causes this Boolean expression to be true, even when i is equal to 5. i is 5 when the loop body is executed for the last time, prompting an IndexOutOfRangeException. To fix the problem, we simply need to exchange the less-than-or-equal-to operator <= with the less-than operator <.

2) Relying solely on the user to end the entry of values into an array before its end is reached will sooner or later invite the user to cause an out of range error.

Loops are frequently utilized to let a user input a list of values into an array. Lines 13 18 of Listing 10.2 represent an example of this functionality. The user is in this case forced to assign a value to every single array element (5 elements in our example). Sometimes, however, the user might want to enter less than this total number of values and, thus, terminate the input phase before the end of the array has been reached. How can this be implemented?

One possible strategy is to use a sentinel value (also called a dummy value or a signal value).

A sentinel value is a special value signaling to the program that the user has ended the entry of values. By typing in the sentinel value after all the proper values have been entered, the user is able to control the number of values he or she wants to enter into an array.

For this technique to work, the sentinel value must be distinctly different from the range of values normally entered into the array. For example, if the values are all expected to be positive, the sentinel value could be a negative number.

Let's see how lines 13 18 of Listing 10.2 can be changed by applying a sentinel value to let the user determine the number of values entered. Listing 10.3 provides the pseudocode outlining a first flawed attempt to implement this logic. Any account balance is presumed to be positive, permitting us to apply a negative value as a sentinel value. This is reflected in line 2 that terminates the loop if the entered value is negative. The flawed logic resides in the fact that the user is solely responsible for terminating the input loop, permitting him or her to attempt to insert more values than there are array elements and causing an out of range exception.

Listing 10.3 Pseudocode Demonstrating Incorrect Use of Sentinel Value
01:  Enter the value for the first array element 02:  While the user did not enter a negative value 03:  { 04:        Assign this value into the corresponding array element. 05:        Add one to the index counter 06:        Enter the next value 07: } 

The program of Listing 10.4 is similar in functionality to that of Listing 10.2, but utilizes the logic presented in the pseudocode of Listing 10.3 to let the user enter values into the array elements of accountBalances (see lines 12 23). The user can end the data entry at any point before all five values have been entered, simply by entering a negative value. This works fine (see Sample output 1) as long as the user remembers to enter the sentinel value (in this case minus one) before the end of the array has been reached. However, in Sample output 2, the user forgets to end the data entry after inputting the fifth value and attempts to enter a sixth value (500 in this case). The .NET runtime responds by generating an IndexOutOfRangeException as reported on the console.

Listing 10.4 BalancesIncorrectSentinel.cs
01: using System; 02: 03: class BalancesIncorrectSentinel 04: { 05:     public static void Main() 06:     { 07:         const decimal interestRate = 0.1m; 08:         decimal [] accountBalances; 09:         decimal newValue; 10:         accountBalances = new decimal [5]; 11: 12:         Console.WriteLine("Please enter max five account balances, " + 13:             "terminate with negative value:"); 14:         Console.Write("Enter balance 1: "); 15:         newValue = Convert.ToDecimal(Console.ReadLine()); 16:         int j = 0; 17:         while (newValue >= 0) 18:         { 19:             accountBalances[j] = newValue; 20:             j++; 21:             Console.Write("Enter balance { 0} : ", (j + 1)); 22:             newValue = Convert.ToDecimal(Console.ReadLine()); 23:         } 24: 25:         Console.WriteLine("\nAccount balances after adding interest\n"); 26:         for (int i = 0; i < accountBalances.Length; i++) 27:         { 28:             accountBalances[i] = accountBalances[i] + (accountBalances[i] *  graphics/ccc.gifinterestRate); 29:             Console.WriteLine("Account balance with index { 0} : { 1:C} ", i,  graphics/ccc.gifaccountBalances[i]); 30:         } 31:     } 32: } 

Sample output 1:

 Please enter max five account balances, terminate with negative value: Enter balance 1: 1000<enter> Enter balance 2: 3000<enter> Enter balance 3: 2000<enter> Enter balance 4: -1<enter> Account balances after adding interest Account balance with index 0: $1,100.00 Account balance with index 1: $3,300.00 Account balance with index 2: $2,200.00 Account balance with index 3: $0.00 Account balance with index 4: $0.00 

Sample output 2:

 Please enter max five account balances, terminate with negative value: Enter balance 1: 3000<enter> Enter balance 2: 200<enter> Enter balance 3: 5000<enter> Enter balance 4: 300<enter> Enter balance 5: 1000<enter> Enter balance 6: 500<enter> Exception occurred: System.IndexOutOfRangeException: An exception of type System.IndexOutOfRangeException was thrown at BalancesIncorrectSentinel.Main() 

Note

graphics/common.gif

To help you determine where in the program an exception is thrown, the name of the method (in this case, BalancesIncorrectSentinel.Main()) is printed on the console as part of the error message.


Lines 17 23 utilize a while loop to enter values into accountBalances. Whenever newValue >= 0 is false, meaning when the user enters a negative value, the loop is terminated. Even though the counter j is incremented for every repetition of the loop, it is not involved in the loop condition. The sentinel value is the sole value responsible for the termination of the loop. The number of repetitions of the loop body is unknown prior to the execution of this while loop, which, for that reason, is called an indefinite loop.

If a sentinel value is the sole factor controlling the termination of a loop, as in lines 16 23 of Listing 10.4, the number of repetitions is not known prior to its execution. The loop is thus often referred to as an indefinite loop.

Fortunately, there is a simple remedy to rectify the problem of lines 16 23. By using the counter j in the loop condition, we can let the repetitions stop if either the user enters a negative value, or j is greater than or equal to the length of accountBalances. This has been done in Listing 10.5 by using a break statement (line 22), which is executed to terminate the loop if j >= accountBalances.Length is true in line 21.

Listing 10.5 Improving Lines 16 23 of Listing 10.4
16: int j = 0; 17: while (newValue >= 0) 18: { 19:     accountBalances[j] = newValue; 20:     j++; 21:     if (j >= accountBalances.Length) 22:         break; 23:     Console.Write("Enter balance { 0} : ", (j + 1)); 24:     newValue = Convert.ToDecimal(Console.ReadLine()); 25: } 

The Fixed Length of Arrays

graphics/common.gif

After an array object has been created, its length cannot be changed. So the following line

 decimal [] accountBalances = new decimal[5]; 

creates an array object that will have a fixed length of 5 throughout its lifetime.

Therefore, the array is not suited to represent collections for which the number of elements varies during runtime.

For example, a program written for a real world bank that manages a fluctuating number of account balances (as opposed to our simulated bank used in the examples here with a fixed number of account balances) would not be served well by attempting to represent the account balances with an array. The .NET Framework contains data structures in its System.Collections namespace that are tailor-made to hold collections of similar elements, such as the array, and with an ability to dynamically grow or shrink during runtime.



   


C# Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 286
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net