Chapter 8
Essentials of Essentials of Fortran-90/95/2003
Arrays

Math 60 -- D. C. Smolarski, S.J.
Santa Clara University, Department of Mathematics and Computer Science

[Return to Math 60 Homepage | Return to Brief Contents Page]
[Return to Full Contents Page]

Contents


8.1 Overview

As with other major languages, arrays in Fortran are a way of storing many pieces of information (i.e., associating a number of variables) under one name. The concept corresponds to the use of subscripted variables in mathematics, e.g., x1, x2, ... , x10. Variables are associated together because of one name, but they can be distinguished because of different subscripts.

8.2 Declaring One Dimension Arrays

In Fortran, array variables must always be declared before use. Any integer is permitted as the starting subscript value. If no starting value is specified, it is assumed to be one.

Arrays may be declared in several different ways.

One may declare an array in a regular type statement, associating the array dimension with the variable name. For example,

      REAL :: A(100)
This statement would declare the variable A to be an array of type REAL with 100 separate memory units. The subscript values range from 1 (default initial value in Fortran) to 100. (In this style of declaration, one may omit the double colon.)

One may, alternatively, use a DIMENSION attribute, whose purpose is to assign arrays their dimensions. For example,

      REAL, DIMENSION(1:100) :: A
This explicitly indicates that A is an array whose single dimension starts at 1 and ends at 100 (inclusive).

There are other styles of declaring arrays dating to earlier versions of Fortran. Some of these involve using default typing for the arrays but using a DIMENSION statement to specify the size. The use of an separate DIMENSION statement is discouraged now (cf. section 18.10).

NOTES:

  1. One should not use different declaration styles within the same declaration statement. For example, one should never put A(100) on a right side of the double colon in a type declaration statement and also include a DIMENSION(1:100) attribute as well.
  2. To have the low subscript of an array start at a number other than one, when declaring the array one may indicate both the low and high subscripts, separated by a colon, within the parentheses after the variable name or after the DIMENSION keyword. For example, if one wanted the array B to be of size 20, but the low subscript to be -10, one would write
    	REAL B(-10:9)
    
    or
            REAL, DIMENSION(-10:9) :: B
    
  3. Small, one-dimensional arrays can be initialized by means of what is called an array constructor. (Such a constructor can also appear elsewhere in a program, e.g., assignment statements.) An array constructor is a list of numeric values, separated by commas and enclosed by (/ and /). Thus we can write:
            REAL :: D(4) = (/2.5,3.2,1.0,5.2/)
    
    One can also use a implied DO-loop form when multiple variables will be initialized to the same value, for example:
            INTEGER :: A(100) = (/ (0 , I=1,100) /)
    
    Note that the value to be "multiplied" along with the implied DO-loop construction must be enclosed within its own set of parentheses within the array constructor.

    Although small arrays of higher dimensions cannot be initialized directly by an array constructor, they may be initialized by using the RESHAPE function (see section 8.11 below) with an array constructor as the SOURCE array.

    Fortran-2003 permits the use of square brackets, [ and ], to enclose the values of an array constructor in place of (/ and /).

8.3 Accessing Array Elements

One accesses an element of an array by indicating the array name followed by the subscript in parentheses. The subscript may be an integer constant, integer variable, or integer expression. For example,
	A = B(2) + B(3)*4.0
NOTE: Currently, any INTEGER expression may be used. In older versions of Fortran, however, the subscript expression had to have a special form in which constant multipliers preceded variables and products preceded additions or subtractions.

8.4 Sample Program Segment

The following segment shows the standard code that is used to add up the elements of an array B (with 10 elements) and store the sum in variable SUM.
	SUM = 0.0
	DO I=1,10
	   SUM = SUM + B(I)
	END DO

8.5 Warning: Misleading Error Messages

Fortran uses parentheses in at least three different ways: (1) to enclose array subscripts, (2) in arithmetic expressions, and (3) to enclose function and subroutine arguments. The compiler can confuse these uses when the programmer makes an error, for example, by forgetting a required array declaration, or by mis-typing a variable name, or in some other way.

Undeclared arrays can lead to confusing error messages from some compilers. For example, suppose a programmer intended B to be an array and uses B in an assignment statement, e.g., A = 2*B(I). Suppose that, by oversight, B was not declared as an array. The compiler should detect an error at this point, and many compilers will give the error message: ``unknown FUNCTION B encountered.'' To understand the origin of this error message, one should examine the form of an array element, e.g., B(I). It is an identifier followed by another identifier within parentheses. Since this form is the same form used in a function call, e.g., COS(THETA) or SQRT(C), most compilers consider the first identifier (e.g., B) actually to be a function. The compiler then looks for the (non-existent) function in its lists of library and user-defined functions, and finding none by that name, gives an ``unknown function'' error message.

Another common error may occur when a programmer forgets to include the Fortran multiplication sign (*) between a variable and an expression surrounded by parentheses. For example, one might type

	A = 2.0*B(I+J-3)
rather than
	A = 2.0*B*(I+J-3)
and, since what was typed looks like a function call or an array, as in the previous case one may get an error message which seems to be unrelated to the actual error.

8.6 Higher Dimensional Arrays

To declare a two or higher dimensional array, one lists the identifier with all the high end subscripts (separated by commas) in a variable type statement (or use a DIMENSION attribute). If the subscripts will not all start at one, the programmer can follow the same pattern shown earlier in the one dimension case, namely, indicate both low and high subscripts separated by a colon, for each dimension. For example,
	REAL NUM(3,3,5), NAMES(5:10,1:30)
or
        REAL, DIMENSION(1:3,1:3,1:5) :: NUM
	REAL, DIMENSION(5:10,1:30)   :: NAMES
In these examples, NUM is a three-dimensional array with 3×3×5 = 45 variables declared, and NAMES is a two-dimensional array with 6×30 = 180 variables declared.

Fortran-90/95/2003 allows a maximum of 7 subscripts (very early versions allowed a maximum of 3 subscripts). Some local implementations, however, may allow more.

Two dimensional arrays are frequently used as tables to store information in rows and columns. The first subscript is usually associated with the row, and the second subscript is associated with the column. Two dimensional arrays are regularly used to store matrices used in mathematical problems.

Three dimensional arrays are often visualized as having rows, columns and levels, but no suitable visualization exists for four or higher dimension arrays.

8.7 Storage of Multi-Dimensional Arrays

The way that two and higher dimensional arrays are stored internally in computer memory can be important in certain special situations, for example, input and output.

Fortran stores arrays in what is called "column major" order. In other words, using the two-dimensional case as an example, the first column of the table (or matrix) is totally stored in consecutive memory locations before the second column is stored, and the second column before the third, etc.

For example, if L were a two dimension array dimensioned as follows,

       INTEGER :: L(3,4)
it may be visualized as a table or matrix with 3 rows and 4 columns, as follows:
	L(1,1)	L(1,2)	L(1,3)	L(1,4)
	L(2,1)	L(2,2)	L(2,3)	L(2,4)
	L(3,1)	L(3,2)	L(3,3)	L(3,4)
The Fortran order of storage is L(1,1) followed by L(2,1) (which is the second element in the first column), followed by L(3,1) (which is the third and last element in the first column), followed by L(1,2) (which is the first element in the next column), followed by L(2,2), L(3,2), L(1,3), L(2,3), L(3,3), L(1,4), L(2,4), and finally L(3,4).

Fortran "column major" storage differs from the usual intuitive approach most people take in identifying the "next" element in a two-dimensional array (or matrix). The "human" order is "row major," i.e., the first row is stored (consecutively) before the next row. (This is the same "order" in which words in a paragraph are read in English.) In the previous example, the (human or row-major) order of the storage of elements of L would be L(1,1), L(1,2), L(1,3), L(1,4), L(2,1), L(2,2), L(2,3), L(2,4), L(3,1), L(3,2), L(3,3), and finally L(3,4). This is also the internal order used in several other computer languages, e.g., C/C++. This difference between the two orders can cause problems if one writes a Fortran subprogram which is to be used with a main program written in a different language and one wishes to pass arrays back and forth.

The standard rule of thumb used to decipher the order in which an array of any dimension is stored by Fortran is this: the first subscript varies the fastest. That is, in determining the changing of subscripts, the order is opposite from the order used on a digital clock or a mileage odometer in a car. For example, suppose A was declared as follows:

	REAL A(3,4,2)
then, in memory, the next location after the one containing A(1,3,1) would contain A(2,3,1), then A(3,3,1), then A(1,4,1). After A(3,4,1) would come A(1,1,2), and so on.

8.8 One-Dimension Arrays And Input And Output

The standard rule for Fortran input/output is that each invocation of a READ/WRITE starts a new line/card according to the format specified in the related FORMAT statement (cf. section 3.2). Therefore, the following program would read five lines, one number per line:
	DO I=1,5,1
	  READ(5,101) A(I)
	  101 FORMAT(F10.5)
	END DO
If, however, one wanted to read more than one bit of information on one line, that fact needs to be indicated in the READ statement (and associated FORMAT statement).

Since, (following the standard rule) each READ statement (or each re-use in a loop of a READ statement) starts a new input line, if one wanted to read five numbers on one line, one would have to use only one READ statement and write something similar to the following:

	READ(5,101) A(1),A(2),A(3),A(4),A(5)
	101 FORMAT(5F10.5)
One can simplify the notation by using an "implied DO-loop" construction as follows:
	READ(5,101) (A(I),I=1,5)
	101 FORMAT(5F10.5)
Note that the DO-loop variable and its limits (with a step if desired) must be associated with a variable by means of parentheses. Essentially, the implied DO-loop expression is "expanded" to full form, and then the READ is executed.

If the array A is declared to be of dimension 5, one can omit the subscript and implied DO-loop entirely. For example, one can write:

	READ(5,101) A
	101 FORMAT(5F10.5)
In this last case, (assuming that A was properly declared as an array) Fortran would know that A is an array rather than a scalar variable, and automatically "expand" A to its full dimension before executing the READ. Thus the three different examples given above would be equivalent in their actions!

NOTE: this same discussion also holds for output using WRITE statements.

8.9 Multi-Dimensional Arrays And Input And Output

Multi-dimensional arrays can cause programmers problems when these arrays are used in automatic input and output, unless the programmer remembers that the Fortran storage order differs from the programmer's "human" order, as described earlier in section 8.7.

Suppose A is dimensioned as REAL A(3,4). Then A has three "rows" and four "columns" if we think in terms of a table or matrix. Since Fortran stores the array in column order, this is the order which is used in "automatic" input or output (i.e., the third example in the previous section, the one without any subscripts). Therefore, if one omits the subscript, for example:

	WRITE(*,101) A
then the order in which the elements of the two dimensional array would appear would be Fortran column order (rather than "human" row order). (Cf. the order for the elements of array L in section 8.7 above.)

To "force" row order, one needs to use implied DO-loops, for example,

	WRITE(*,101) ((A(I,J),J=1,4),I=1,3)
As with nested DO-loops, the nested implied DO-loops force the inner loop to be done completely (4 times) for each pass of the outer loop (cf. section 6.9). Since the inner loop controls the second subscript, the effect is that the second subscript varies faster than the first subscript, thereby producing row order.

8.10 Overuse Of Formats With Arrays

As discussed earlier in section 4.6, if there are more variables than format field descriptors, then the FORMAT statement is re-used until all the variables are either read into or written out. This feature can be useful when dealing with arrays.

The following example, with arrays, is similar to the example in section 4.6.

	WRITE(6,102) (A(I),B(I),I=1,10)
	102 FORMAT(1X,2F20.5)
As in the example in section 4.6, the 102 FORMAT statement only provides for two numeric fields. The WRITE statement, however, in effect contains 20 variables which it wants to write out. Therefore, the formatting edit descriptors in the FORMAT get re-used 10 times, each time starting a new line. If, however, the edit descriptors were changed to allow three numbers per line, the output would consist of seven lines, with three variables per line for the first six lines, and two variables on the last line. The order of the variables would be the same, i.e., A(1), then B(1), then A(2), then B(2), then A(3), then B(3), etc.

8.11 Using Arrays In Fortran-90/95

Fortran-90/95/2003 has significantly enhanced its capabilities for using arrays by permitting arithmetic operations on entire arrays.

Array identifiers may now be used in arithmetic expressions without any subscript when the same operation is to be performed on every element of the array. The presupposition, however, is that all arrays in such arithmetic expressions have the same number of dimensions and extent (size) of each dimension.

For example, if A, B and C are all dimensioned as follows,

       REAL, DIMENSION(3,4) :: A,B,C

then the following is legitimate Fortran-90/95/2003 code:

       C = A + B

This statement adds corresponding items in arrays A and B and stores the value in array C. To do this same operation in FORTRAN-77, one would need two nested DO-loops. Note that

       C = A * B
multiplies corresponding elements in array A and array B together and does not perform what is commonly called "matrix multiplication." Matrix multiplication is provided by means of a new intrinsic function in Fortran-90/95/2003 (discussed below in section 8.13).

Other arithmetic operations on arrays also are permitted. For example, C = A * 3.0 indicates that every element in A is multiplied by 3.0 and the resulting value is stored as the corresponding element in C.

Intrinsic arithmetic functions may also be called on arrays, indicating that the operation is performed on every element in the array. For example, C = SQRT(A) invokes the SQRT function on every element in array A and stores the resulting values as the corresponding elements in C.

8.12 The WHERE Construct

The WHERE construct is a Fortran-90/95/2003 statement used specifically with arrays. It is similar to an IF statement but with these differences: The array used in the mask must match the shape of the arrays used in the body of the WHERE structure. An ELSEWHERE clause is optional.

The action of the WHERE construct is as follows: For each element of the arrays in the construct, if the logical value determined by the mask evaluates to true, the statements within the structure are performed using the corresponding element of the arrays contained in those statements. (If the mask value evaluates to false, the statements in the ELSEWHERE section are performed, if this section exists.)

For example,

      WHERE (A > 0.0)
        B = SQRT(A)
      ELSEWHERE
        B = 0.0
      ENDWHERE
will store in matrix B the square root of the values stored in corresponding locations in matrix A, where those values are positive (since that is what the mask expression, A > 0.0, prescribes). For values that are zero or negative, a zero is stored in the appropriate location in matrix B.

If there is no ELSEWHERE clause and only one assignment statement, the WHERE construct can be condensed to a single line, for example:

      WHERE (A > 0.0) B = SQRT(A)
Fortran-95/2003 has extended the original Fortran-90 WHERE statement to permit additional masks to be associated with any ELSEWHERE statement contained in the structure. The mask must be of the same shape as that used in the WHERE statement. If there are multiple ELSEWHERE statements, only the last one may be left without a mask. Fortran-95/2003 also permits the nesting of WHERE statements and labeling as with DO loops.

8.13 Array Functions

Fortran-90/95/2003 has introduced, among its intrinsic functions, new functions for arrays. Among the more significant functions are the following:

8.14 Allocatable Arrays

There are situations in which a program or a subprogram may use, as an argument for a subprogram, an array whose size may not be the same at each call of the subprogram. Fortran-90/95/2003 permits the program to allocate space dynamically for such an array (and in other situations) during execution.

Any array to be allocated explicitly must be designated as such when declared. This is done by including the keyword ALLOCATABLE before the double colon in the type declaration and indicating the dimension by using colons in place of numbers. For example,

      REAL, ALLOCATABLE :: A(:,:)
indicates that A is an allocatable array with two dimensions. Then, before the array is actually used and assigned values by the program segment, the code invokes
      ALLOCATE( A(N,M) )
where N and M can be actual integer numbers or at the least have been assigned values before that statement. When the array is no longer needed, the code invokes
      DEALLOCATE ( A )

8.15 Additional Features

Fortran-90/95/2003 includes other specialized features to deal with arrays. For instance, it is possible to specify sections of an array in arithmetic expressions and assignments. For example, A(2:5) indicates elements 2, 3, 4, and 5 of array A. A step size (or "stride") may be included as a third integer after the subscript range. Thus, A(2:6:2) indicates elements 2, 4, and 6 of array A (the subscript starts at 2 and ends at 6, incrementing by 2's). One can also use a mask (cf. section 8.12) to designate which elements of an array to use.

Further details on these and on other array features and functions provided in Fortran-90/95/2003 can be found in specialized books.


This page is maintained by Dennis C. Smolarski, S.J. dsmolarski@math.scu.edu
© Copyright 1998-2005 Dennis C. Smolarski, SJ, All rights reserved.
Last changed: 29 June 2005.