Arrays

Manipulating a multitude of values in so many variables is very unwieldy. Not to mention that each variable has to be declared, and when you write the program you may not know how many values you will be handling. Hence arrays.

One-dimensional arrays

A one-dimensional array is a row of values of some type. You access the fifth value in an array ar for instance by ar(5). You can view successive array elements as lying in sequence in the memory of the computer, and that is in fact a correct view.

Declaration

The declaration of an array involves its type and size. You can declare an array in one statement, eg
      REAL ar(20)
or declaring type and size separately, eg
      DIMENSION ar(20)
      REAL ar
where the type declaration can stay implicit. The size of an array is statically determined: you cannot read it in and then create the array; you have to declare it `large enough' in the source. The array bound can be an expression, but it can involve only constants or named constants.

The first element of the array is ar(1); if your problem is more naturally expressed with a lower bound other than one, you can declare the array as

      DIMENSION ar(lo:hi)
where lo and hi are integers.

Use

There are no operations on the whole array; you can only access the elements one at a time by using subscript expressions, eg
      x(i) = 5.1
      k = 2*iarr(5)-iarr(ip(3))
In the second line an integer array ip was used to provide the subscript in the array iarr.

The subscript expressions used have to have a value between the upper and lower bound of the array. However, the compiler cannot determine whether this will be the case, since subscript expressions can be computed during the run of the program.

It is then your responsibility to ensure that you will not take elements outside the bounds of the array. Doing so will give undefined results, or may crash your program. Often, compilers have an option to let bounds be tested during runtime. Since this slows down your program considerably, it is something you typically do during the testing phase of the program.

Implied DO loops

It would be possible to output an array as
      DO 10 i=1,n
         PRINT *,a(i)
10    CONTINUE
but this leads to n lines each with one number on them. To write the array values on a single line, one needs the `implied DO loop' construct:
      PRINT *,(a(i),i=1,n)
This can also be used for input of array elements.

Implied do loops can also be used in array initialisation.

F90: slices.

Arrays as subroutine argument

Passing an array as actual argument

If a subprogram has a scalar argument, eg
      SUBROUTINE s(x)
the routine can be called with an array element as argument:
      DIMENSION ar(10)
      ...
      CALL s(ar(5))
and the routine can use its argument the same way as when a variable had been passed.

If the subprogram assigns a value to its scalar argument, and the actual argument is an arrary element:

      SUBROUTINE s(x)
      x = 2.
      RETURN
      END
      DIMENSION ar(10)
      ...
      CALL s(ar(5))
then the effect in the calling program unit would be the same as
      ar(5) = 2.

Accepting an array as formal argument

Above we considered passing an array element to a subprogram that has a scalar formal argument. However, subprograms can also have array formal arguments. Example:
      SUBROUTINE s(x)
      DIMENSION x(7)
If the array size is declared explicitly in the subprogram, it is the responsibility of the programmer to make sure that a sufficiently large array is passed to the subprogram:
      DIMENSION ar(8)
      ...
      CALL s(ar)
Instead of calling the subprogram with the array name, one could supply another element:
      CALL s(ar(2))
In this example that corresponds to supplying the segment with the seven elements ar(2)..ar(8) to the subprogram. This also shows that you can tinker with the bounds of an array when passing it: the subroutine could equally validly have been written as
      SUBROUTINE s(x)
      DIMENSION x(-3:3)

Instead of declaring the size of the array explicitly in the subprogram, which is usually too restrictive, one can pass the size as argument:
      CALL s(ar,8)
      ...
      SUBROUTINE s(x,n)
      DIMENSION x(n)
Since the size of this array can be different each time the subroutine is called, this is called an `adjustable array'. Instead of passing the size as argument, it can also be kept in a COMMON block.

It is even allowed not to declare the size of the array at all in the subprogram, but merely to declare that it is an array:

      SUBROUTINE s(x)
      DIMENSION x(*)
Assumed size, size passed, adjustable array, assignment to

Passing by reference revisited

When an array or array section is passed to a subprogram, Fortran does not make a copy of that array; instead the same mechanism of passing by reference is used that is employed for scalar argument. To see an implication of this, consider the subroutine
      SUBROUTINE copy(x,y,n)
      REAL x(n),y(n)
      DO 10 i=1,n
         y(i) = x(i)
10    CONTINUE
      RETURN
      END
and consider the difference between the following calls:
      DIMENSION z(11)
      CALL copy(z(2),z(1),10)
or
      CALL copy(z(1),z(2),10)
The first call will have the intended effect of moving a block of data, but the second one has the effect of setting all array elements equal to the first.

Initialisation in DATA statement

The DATA statement can be used in various ways to initialise an array. To initialise an array, you can either write out values
      DIMENSION X(5)
      DATA X/1.1,2.2,3.3,4.4,5.5/
or use repeated values
      DATA X/5*1.2/
if all elements are to receive the same value. Parts of an array can be initialised by using implied DO loops:
      DATA x(1),(x(i),i=2,5)/1.,4*2./

Multi-dimensional arrays

In order to program structures such as matrices, Fortran can have multi-dimensional arrays. For example, if you declare
      DIMENSION a(10,20)
then a 10 by 20 matrix can be stored in a. You refer to elements in a multi-dimensional array by supplying enough subscripts:
      a(5,12) = 17.2
For any or all of the dimensions upper and lower bounds can be specified separately:
      DIMENSION a(18,-1:1,0:15)

The maximum number of dimensions can be seven (this used to be three in Fortran66).

Storage, traversal

The elements of a one-dimensional array are simply laid out in memory in sequence, from element number one (or whatever the lower bound is) to the last. Multi-dimensional arrays could conceivably be stored in a number of ways, eg with rows or with columns occupying contiguous memory. In practice all Fortran implementations adhere to one scheme: columns are stored contiguously.

That is, if a matrix is declared as

      INTEGER mat(2,2)
then its elements are, in sequence:
    mat(1,1), mat(2,1), mat(1,2), mat(2,2)
This is often described as "the leftmost index varies fastest".

This storage scheme allows you to play tricks with reshaping arrays by passing them as routine arguments. There are also practical implications when traversing the array. By letting an index run over the second and higher dimensions, the number of cache misses, memory bank conflicts, page faults, can greatly increase.

Multi-dimensional arrays as arguments

As a consequence of the storage scheme for multi-dimensional matrices, you can pass lower dimensional parts of an array:
      DIMENSION ar(3,4,5)
      ...
      DO 10 i=1,5
         CALL subr(ar(1,1,i))
      ...
      SUBROUTINE subr(x)
      DIMENSION x(3,4)
There are more tricks with array dimensions. For instance, with the subroutine as above, the same main program could have been written as
      DIMENSION ar(12,5)
      ...
      DO 10 i=1,5
         CALL subr(ar(1,i))
or even
      DIMENSION ar(60)
      ...
      DO 10 i=1,5
         CALL subr(ar(1+(i-1)*12))
The `star notation' for arrays passed as arguments can be used for higher dimensional arrays, but only for the right-most dimension. Eg, the above subroutine could have been written:
      SUBROUTINE subr(x)
      DIMENSION x(3,4,*)

Programming techniques

Leading dimension vs leading size, no longer in Fortran90.

Multidimensional bound checking
Exercise: simulate multi-dim in one dimensional, avoid recomputing index