ZetCode

Fortran arrays

last modified November 14, 2026

An array is a numbered collection of values of the same type, and Fortran treats it as a first-class object: an array can be assigned as a whole, added to another array element by element, sliced into a section, and passed to a procedure as one argument. The language was designed around arrays for numerical work, so the syntax is compact and the compiler can optimise the loops that the array expressions describe. This article shows how to declare arrays, fill them with constructors, select parts of them, and how their elements are laid out in memory.

Declaring arrays

An array declaration puts the shape in parentheses after the name: integer :: scores(5) declares an array of five integers, and real :: grid(3, 4) a two-dimensional array of twelve reals. The numbers are the extents of each dimension, and the lower bound defaults to 1, so scores has the valid indices 1 to 5. The same declaration can be written with the dimension attribute, but the parenthesised form is shorter and more common.

arr1.f90
program arr1
    implicit none
    integer :: scores(5)
    real :: grid(3, 4)
    integer :: i

    scores = [10, 20, 30, 40, 50]
    grid = 0.0

    print *, 'size of scores:', size(scores)
    print *, 'shape of grid: ', shape(grid)
    do i = 1, 5
        print *, 'score', i, ':', scores(i)
    end do
end program arr1

The assignment of the constructor [10, 20, 30, 40, 50] fills all five elements at once, and grid = 0.0 sets every element of the matrix to zero with a single scalar value. size returns the number of elements in the whole array, while shape returns an array of the extents, one per dimension. The loop prints the elements one by one, which is still the clearest way to label them.

$ gfortran arr1.f90 -o arr1
$ ./arr1
 size of scores:           5
 shape of grid:            3           4
 score           1 :          10
 score           2 :          20
 score           3 :          30
 score           4 :          40
 score           5 :          50

Array constructors

A constructor is a bracketed list of values, written [1, 2, 3] in Fortran 2003 and later, or (/1, 2, 3/) in older code. An implied do inside the brackets generates values from a loop, which is how a regular sequence is built without writing every element. The reshape intrinsic turns a one-dimensional constructor into a matrix by reading the values in array element order.

arr2.f90
program arr2
    implicit none
    integer :: a(5)
    integer :: squares(5)
    integer :: m(2, 3)
    integer :: i

    a = [1, 2, 3, 4, 5]
    squares = [(i * i, i = 1, 5)]
    m = reshape([1, 2, 3, 4, 5, 6], [2, 3])

    print *, a
    print *, squares
    print *, m(1, :)
    print *, m(2, :)
end program arr2

The second constructor computes the squares of 1 to 5, and the result is assigned to an array of exactly five elements. reshape gives the six values the shape [2, 3]: the first three fill the first column and the last three the second, so the matrix is [[1, 3, 5], [2, 4, 6]] when written as rows. The two m(1, :) and m(2, :) sections confirm that: the first row holds 1, 3, 5 and the second row 2, 4, 6.

$ gfortran arr2.f90 -o arr2
$ ./arr2
           1           2           3           4           5
           1           4           9          16          25
           1           3           5
           2           4           6

Sections

A section selects a part of an array with a triplet first:last:stride. Any of the three parts may be omitted: a(2:4) takes the elements at indices 2, 3 and 4, a(3:) takes everything from index 3 to the end, and a(:2) takes the first two elements. A negative stride walks backwards, and the default stride is 1. A section is an array in its own right, so it can be printed, assigned or passed to a procedure.

arr3.f90
program arr3
    implicit none
    integer :: a(8)
    integer :: i

    a = [(i, i = 1, 8)]
    print *, a(2:4)
    print *, a(1:8:2)
    print *, a(8:1:-2)
    print *, a(3:)
    print *, a(:2)
end program arr3

The array holds the values 1 to 8. The first section prints 2, 3 and 4; the second takes every second element and prints 1, 3, 5 and 7; the third walks back from 8 with a step of 2 and prints 8, 6, 4 and 2. The last two show the open-ended forms, which print 3 to 8 and 1 to 2 respectively. The bounds are inclusive at both ends, unlike the square brackets of some other languages.

$ gfortran arr3.f90 -o arr3
$ ./arr3
           2           3           4
           1           3           5           7
           8           6           4           2
           3           4           5           6           7           8
           1           2

Whole-array operations

Arrays of the same shape combine element by element with the ordinary arithmetic operators, and a scalar combines with every element. An intrinsic function applied to an array works element by element as well, so sqrt(x) is an array of square roots of the same shape. The reduction functions sum, maxval and minval collapse the whole array to one value; the next article describes those in detail. Assignment between arrays requires identical shapes unless the left side is allocatable.

arr4.f90
program arr4
    implicit none
    integer :: a(4), b(4)
    real :: x(4)

    a = [1, 2, 3, 4]
    b = [10, 20, 30, 40]
    x = [1.0, 4.0, 9.0, 16.0]

    print *, a + b
    print *, 3 * a
    print *, b - a
    print *, a * b
    print *, sqrt(x)
    print *, sum(a), maxval(b), minval(b)
end program arr4

The first four lines show the four arithmetic operators applied pairwise: 11, 22, 33, 44; then 3, 6, 9, 12; then 9, 18, 27, 36; and then 10, 40, 90, 160. sqrt(x) turns the stored squares into 1, 2, 3 and 4, though they are printed as reals. The last line reduces the arrays to the values 10, 40 and 10. No loop appears anywhere in the program.

$ gfortran arr4.f90 -o arr4
$ ./arr4
          11          22          33          44
           3           6           9          12
           9          18          27          36
          10          40          90         160
   1.00000000       2.00000000       3.00000000       4.00000000    
          10          40          10

Column-major order

Fortran stores a two-dimensional array by columns: the first index varies fastest, so all elements of column 1 come first, then all of column 2, and so on. reshape, whole-array input and output, and the array constructors all use this array element order. The result surprises programmers arriving from C, where the last index varies fastest. Printing a matrix row by row therefore needs an implied do that runs over the second index.

arr5.f90
program arr5
    implicit none
    integer :: m(3, 2)
    integer :: v(6)
    integer :: i, j

    v = [1, 2, 3, 4, 5, 6]
    m = reshape(v, [3, 2])

    do i = 1, 3
        print *, (m(i, j), j = 1, 2)
    end do
    print *, 'storage order:'
    print *, m
    print *, 'transposed rows:'
    do j = 1, 2
        print *, (m(i, j), i = 1, 3)
    end do
end program arr5

The matrix has three rows and two columns, and the printed rows are 1, 4 and then 2, 5 and then 3, 6, because the constructor filled the columns first. Printing the whole array shows the storage order 1 to 6, which is not the order in which the values appear on the screen. The last loop walks the other way, taking one element from each row, and prints the two columns as 1, 2, 3 and 4, 5, 6. The transpose intrinsic produces a new array with the indices exchanged.

$ gfortran arr5.f90 -o arr5
$ ./arr5
           1           4
           2           5
           3           6
 storage order:
           1           2           3           4           5           6
 transposed rows:
           1           2           3
           4           5           6

Inquiry functions and lower bounds

size, shape, lbound and ubound report the geometry of an array without examining its values. lbound and ubound return arrays with one entry per dimension, or a scalar when the optional dim argument names a single dimension. The lower bound does not have to be 1: a declaration such as years(1890:2020) keeps the years themselves as indices, which often removes an offset from the code.

arr6.f90
program arr6
    implicit none
    integer :: years(1890:2020)
    integer :: i

    years = [(i, i = 1890, 2020)]
    print *, 'lbound:', lbound(years)
    print *, 'ubound:', ubound(years)
    print *, 'size:  ', size(years)
    print *, 'first: ', years(1890)
    print *, 'last:  ', years(2020)
end program arr6

The array still has 131 elements, because the size is the difference between the bounds plus one, but the valid indices run from 1890 to 2020. lbound and ubound return those two numbers, and the first and last elements are reached with the year as the index. Asking for years(1) would be out of bounds, and with -fcheck=all the program would stop at run time.

$ gfortran arr6.f90 -o arr6
$ ./arr6
 lbound:        1890
 ubound:        2020
 size:           131
 first:         1890
 last:          2020

Shape conformance is checked

An array assignment requires the two sides to have the same shape, and the compiler rejects a mismatch when it can see one. The program below assigns a four-element array to a three-element one. The message names the dimension and both extents, which is usually enough to find the declaration that is wrong.

arr7.f90
program arr7
    implicit none
    integer :: a(3), b(4)

    a = [1, 2, 3]
    b = a
end program arr7

The shapes 3 and 4 differ, so the assignment to b cannot be performed element by element, and the compiler stops with an error instead of copying the first three values and leaving the fourth undefined. A section can be assigned when its shape matches: b(1:3) = a would be legal, and so would a scalar assignment such as b = 0.

$ gfortran arr7.f90 -o arr7
arr7.f90:6:4:

    6 |     b = a
      |    1
Error: Different shape for array assignment at (1) on dimension 1 (4 and 3)

Bounds checking

An index outside the declared bounds is not caught by the compiler when the index is a variable, because its value is only known at run time. The -fcheck=all option adds checks around every array reference, and the program then stops with a message that names the array, the index and the bound. The shorter output comes from -fno-backtrace, which leaves out the call stack that GNU Fortran prints by default.

arr8.f90
program arr8
    implicit none
    integer :: a(3)
    integer :: j

    a = [1, 2, 3]
    j = 4
    print *, a(j)
end program arr8

The program reads a(4), one past the last element. With bounds checking the run ends with a non-zero exit status and the message shown below; without -fcheck=all the read would silently use whatever memory follows the array, which is undefined behaviour and a common source of wrong results in numerical code. Bounds checking is cheap during development and is normally removed in the final optimised build.

$ gfortran -std=f2018 -fcheck=all -fno-backtrace arr8.f90 -o arr8
$ ./arr8
At line 8 of file arr8.f90
Fortran runtime error: Index '4' of dimension 1 of array 'a' above upper bound of 3

Next steps

The next article, Fortran array intrinsics, takes the reduction functions used above and adds masks, locations, packing and the where construct. The allocatable arrays article shows how to give an array its shape at run time, and the array arguments article explains how arrays and sections cross a procedure boundary. The loops article covers the implied do used to print the matrix.

Source

Fortran 2018 standard (J3/18-007r1)

GNU Fortran: SIZE

GNU Fortran: RESHAPE

In this article we have declared one- and two-dimensional arrays, filled them with constructors and implied loops, selected sections with strides, applied operators and intrinsics to whole arrays, seen the column-major storage order, queried the bounds, and watched the compiler and the runtime catch shape and bounds errors. The previous article, Fortran modules, shows where array procedures usually live.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Fortran tutorials.