double precision :: list(LIST_SIZE) integer :: index double precision :: tolerance
An array is a contiguous block of memory. Assuming the base address of list is 1000, the array would map into memory as follows:
Table 23.1. Memory Map of an Array
Address | Content |
---|---|
1000-1007 | list(1) |
1008-1015 | list(2) |
... | ... |
8992-8999 | list(1000) |
The address of an array element with index i is the base-address + (i - lowest-subscript) * (number of memory cells used by 1 element).
E.g., the address of list(i) = 1000 + (i-1) * 8.
address of list(1) = 1000 + (1-1) * 8 = 1000 address of list(2) = 1000 + (2-1) * 8 = 1008 ... address of list(1000) = 1000 + (1000-1) * 8 = 8992
Most compilers don't generate machine code that checks array subscripts. Validating the subscript on every array reference would slow down array access significantly.
The programmer must ensure that their code uses only valid array subscripts!
int list[5], c; for (c = 0; c <= 5; ++c) { list[c] = 0 printf("%d\n", c); }
integer :: list(5), i do i = 1, 6 list(i) = 0 print *, i enddo
This code will overrun the end of the array 'list'.
Assume the base address of list is 4000.
Table 23.2. Memory Map of list and i
Address | Content |
---|---|
4000-4003 | list(1) |
4004-4007 | list(2) |
4008-4011 | list(3) |
4012-4015 | list(4) |
4016-4019 | list(5) |
4020-4023 | i |
When setting list(6), it computes the address 4000 + 4 * (6-1) = 4020, which is the address of the integer variable 'i'. It places a 4 byte integer 0 there, overwriting 'i', and causing the loop to start over.
This demonstrates the value of using named constants or variables to specify array boundaries!
integer :: list(LIST_MAX), i do i = 1, LIST_MAX list(i) = 0 print *, i enddo
It's easy to type 6 when you meant 5, but not so easy to type LIST_MAX+1 when you meant LIST_MAX.
Out-of-bounds array subscripts can cause a variety of problems.
Finally, if you're lucky, a runaway subscript may cause your program to crash. In Unix, this is usually associated with a segmentation fault or a bus error. Both of these errors indicate that the program attempted an illegal memory access.
A segmentation fault means an attempt to access memory in a way that's not allowed (e.g. a memory address not allocated for data), whereas a bus error indicates an attempt to access memory that does not physically exist, an attempt to write to a ROM, etc.
These errors often cause the program to dump a core, which is a snapshot of the program code and data as it appeared in memory at the moment of the crash. The core file can be used by a debugger to pinpoint exactly which statement in the program was executing when the crash occurred. For most Unix compilers, compiling with the -g flag causes more useful information for debuggers to be placed in the executable file. Increasing the optimization level (-O, -O2, -O3) reduces the debugger's ability to pinpoint problems. For more information, consult the documentation on your compiler and debugger.