Array Basics

To implement the solution to printing 1000 numbers in reverse order, we could define 1000 variables, and use 1000 read and print statements:

double  num1, num2, ... num1000

scanf("%lf", &num1);
scanf("%lf", &num2);
...
scanf("%lf", &num1000);

printf("%f\n", num1000);
...
printf("%f\n", num2);
printf("%f\n", num1);
        
double precision :: num1, num2, ... num1000

read *, num1
read *, num2
...
read *, num1000

print *, num1000
...
print *, num2
print *, num1
        

We now have a program of 2000 lines + the overhead code framing out the program, defining the variables, etc. This is clearly more effort than we want to put into a program, and the program is not at all flexible. Where would we be if we needed to do the same for a billion numbers?

An array is a variable that holds multiple values of the same type. Each value in the array is distinguished from the others using an integer index, also known as a subscript. The term subscript comes from mathematics, where we might specify one element from of an array of values such as K0, K1, ... Kn.

In C, we make a variable into an array by providing the number of elements it contains in square brackets following the name. We then select an element by specifying the subscript in square brackets after the name. C subscripts always begin at 0 and end at the array size minus one.

#define LIST_SIZE   1000

double list[LIST_SIZE];

scanf("%lf", &list[0]);
scanf("%lf", &list[1]);
...
scanf("%lf", &list[999]);

printf("%f\n", list[999]);
...
printf("%f\n", list[1]);
printf("%f\n", list[0]);
        

In Fortran, we make a variable into an array by providing the number of elements it contains in parentheses following the name. We then select an element by specifying the subscript in parentheses after the name. By default, Fortran subscripts begin at 1, but we can control this.

module constants
    integer, parameter :: LIST_SIZE=1000
end module

double precision :: list(LIST_SIZE)

read *, list(1)
read *, list(2)
...
read *, list(1000)

print *, list(1000)
...
print *, list(2)
print *, list(1)
        

An array makes it much easier to allocate the space for 1000 double precision values, but we still have 2000 statements to read the list and print it backwards. Can we do better?

The only restriction on subscripts is that the must be integers. They may be constants, variables, or complicated expressions. As long as the value of the subscript is within the range of subscripts for the array, there is no problem.

Most commonly, subscripts are a simple variable which is controlled by a loop that traverses all valid subscripts for the array:

Variables used to subscript an array must have enough range. A Fortran integer(1) or C char variable have a maximum value of +127, so they cannot be used as subscripts for an array of 1000 elements. A Fortran integer (integer(4)) variable has a maximum value of 231-1 (a little over 2 billion), which is enough for most arrays. It is possible on modern computers to have arrays with more than 2 billion elements, so we may sometimes need to use integer(8).

The C header files define an unsigned integer type called size_t which has the same number of bits as a memory address on the underlying hardware. Hence, it is guaranteed to be able to handle an array of any size. This data type should be used for virtually all array subscripts in C.

#define LIST_SIZE   1000

double  list[LIST_SIZE];
size_t  c;

for (c = 0; c < LIST_SIZE; ++c)
    scanf("%lf", &list[c]);
    
for (c = LIST_SIZE-1; c >= 0; --c)
    printf("%f", list[c]);
        
module constants
    integer, parameter :: LIST_SIZE=1000
end module

double precision :: list(LIST_SIZE)
integer :: index

do index = 1, LIST_SIZE
    read *, list(index)
enddo

do index = LIST_SIZE, 1, -1
    print *, list(index)
enddo
        

In Fortran, if we want to define multiple arrays of the same size, we can use the dimension modifier instead of specifying the dimension for every array variable.

double precision, dimension(LIST_SIZE) :: list1, list2, list3
        

Fortran also allows the programmer to choose any starting and ending subscripts desired. The default starting subscript is 1, so the following is equivalent to the examples above:

double precision :: list(1:LIST_SIZE)
        

For some arrays, it may not make sense to use a starting subscript of one. For example, if an array contains the probability of a driver in the U.S. having an accident based on their age, then subscripts less than 15 would be useless, since people under are not allowed to drive.

We should also specify the array dimensions using named constants to make the program more readable and easier to modify.

module constants
    integer, parameter :: MIN_AGE=15, MAX_AGE=120
end module

program insurance
    double precision :: accident_probability(MIN_AGE : MAX_AGE)
    integer :: age
    
    ...
    
    do age = MIN_AGE, MAX_AGE
        print *, accident_probability(age)
    enddo
    
    ...
end program

#include <stdio.h>
#include <sysexits.h>

#define MIN_AGE     15
#define MAX_AGE     120

int     main()
{
    double accident_probability[MAX_AGE - MIN_AGE + 1];
    size_t age;
    
    for (age = MIN_AGE; age <= MAX_AGE; ++age)
        printf("%f\n", accident_probability[age - MIN_AGE]);
    
    return EX_OK;
}