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.
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.
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.
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.
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.
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.
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.
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.
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)
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
List all Fortran tutorials.