The valid range
An array with Length elements has the valid indexes 0 through Length - 1. Any other index is outside the array's bounds. The compiler can't generally know the value of an index, so the code compiles, but the program checks the index each time it runs and throws an IndexOutOfRangeException if it is invalid.
int[] ourArray = {50, 40, 30};
// Valid indexes: 0, 1, 2
Console.WriteLine(ourArray[0]); // 50
Console.WriteLine(ourArray[2]); // 30
// Invalid indexes: 3 and above, and anything negative
Console.WriteLine(ourArray[3]); // throws IndexOutOfRangeException
The message reads Index was outside the bounds of the array. A negative index such as -1 fails the same way. Unlike Python, C# does not count negative indexes from the end. To read from the end, use Length - 1 or the ^1 syntax.
When the exception isn't caught, the program stops at that line, so the lines after it never run. That is why this error is worth avoiding rather than handling: it almost always points to an off-by-one mistake.
The exercise, step by step
The starter code is meant to print the last element of ages, but it throws:
int[] ages = {14, 32, 50, 21};
Console.WriteLine(ages[ages.Length]);
- Find the index.
ages.Lengthis 4. The array's indexes are 0, 1, 2 and 3, so index 4 is one past the end. - Move one step back. The last element is at
Length - 1, which is 3. Changing the index toages.Length - 1reads the value 21.
The fixed program:
int[] ages = {14, 32, 50, 21};
Console.WriteLine(ages[ages.Length - 1]);
It prints:
21
Guarding against the error
You can check the index before using it. The check has two parts, because both ends of the range matter:
int[] ages = {14, 32, 50, 21};
int index = 4;
if (index >= 0 && index < ages.Length) {
Console.WriteLine(ages[index]);
} else {
Console.WriteLine("No such position");
}
This prints No such position. You could also catch the exception with try and catch (IndexOutOfRangeException), but a plain check is simpler and shows your intent. Catching is better saved for failures you can't test for in advance.
In a test or while debugging, read the exception text first. It names the exception type, and the stack trace points to the line number that used the bad index. Then work out which index you passed and compare it with the array's length.
Because the check happens at run time, a program can work for months and fail the first time it receives an unusual input, such as an empty array. Testing small and empty inputs is therefore the best way to find these bugs early.
Common causes
- Using the length as an index. This is the bug above and the most frequent one. The length is a count; the last index is one less.
- Loops that go one step too far. A loop condition of
i <= array.Lengthreaches the invalid index. Usei < array.Length. - Empty arrays. For an array of length 0, even index 0 is invalid, and
array[array.Length - 1]asks for index -1. - Indexes computed from input. When an index comes from a calculation or from the user, check
index >= 0 && index < array.Lengthbefore using it.
Why C# checks bounds
Some languages let a program read past the end of an array and silently return whatever happens to be in memory. C# never does that. Every array access is checked, so a mistake produces a clear exception with a line number, not wrong results that appear much later. The check is the reason the error message exists in the first place.
Things to try
Try these on a four-element array to see which ones throw. The index 0 works, the index 3 works, and the indexes 4 and -1 throw. Then try ages[ages.Length - 1] on an array created with new int[0] and watch it fail at index -1.
Key takeaways
- Valid array indexes run from 0 to
Length - 1. - An invalid index throws
IndexOutOfRangeExceptionat run time, not at compile time. - To get the last element, use
array[array.Length - 1].