ZetCode

Fortran allocatable arrays

last modified November 14, 2026

An allocatable array gets its shape while the program runs, from a value that is only known then: the number of lines in a file, the length of a command-line argument, the size of a grid read from a configuration. The declaration fixes the type and the rank and leaves the bounds open with a colon, and allocate supplies the bounds later. The compiler keeps track of whether the array currently has storage, and modern Fortran reallocates it automatically when an assignment needs a different shape. This article shows allocation, deallocation, error handling, and the idioms that grow an array safely.

Allocating and deallocating

The declaration integer, allocatable :: a(:) creates an array descriptor but no elements. allocate(a(5)) reserves storage for five integers, after which the array can be used like any other, and deallocate(a) releases the storage. The allocated inquiry tells whether the descriptor currently points at storage; it is false before the allocation and true after it.

a1.f90
program a1
    implicit none
    integer, allocatable :: a(:)
    integer :: i

    print *, 'allocated before:', allocated(a)
    allocate(a(5))
    print *, 'allocated after: ', allocated(a)
    a = [(i * 10, i = 1, 5)]
    print *, a
    deallocate(a)
    print *, 'allocated at the end:', allocated(a)
end program a1

Before the allocation the array has no elements and cannot be indexed. After it, the constructor fills the five positions with 10, 20, 30, 40 and 50. The last report is false again, because deallocate returned the memory to the system. Deallocating an array that is not allocated is an error, and so is allocating one twice, which is why the next section checks the status.

$ gfortran a1.f90 -o a1
$ ./a1
 allocated before: F
 allocated after:  T
          10          20          30          40          50
 allocated at the end: F

When allocation fails

allocate accepts stat= and errmsg= arguments. With stat, a failure does not terminate the program; the variable receives zero on success and a non-zero code on failure, and errmsg receives a message that describes the problem. The numerical code is processor-dependent, so a program should test stat /= 0 rather than a particular value, and it should only read errmsg after a failure.

a2.f90
program a2
    implicit none
    integer, allocatable :: a(:)
    integer :: stat
    character(len=80) :: msg

    allocate(a(3), stat=stat, errmsg=msg)
    print *, 'first allocate:  stat =', stat
    allocate(a(4), stat=stat, errmsg=msg)
    print *, 'second allocate: stat =', stat
    print *, trim(msg)
end program a2

The first allocation succeeds, so the status is 0 and the message is not used. The second one tries to allocate the same array again without deallocating it first, which is invalid, so the status becomes non-zero and the message explains the reason. A real program would branch on stat /= 0, report the message, and either stop or pick a smaller size.

$ gfortran a2.f90 -o a2
$ ./a2
 first allocate:  stat =           0
 second allocate: stat =        5014
 Attempt to allocate an allocated object

Copying with source and mold

source= allocates an array with the shape of an existing one and copies its values, so the new array is immediately usable. mold= takes only the type, kind and shape from the argument; the contents of the new array are undefined until they are assigned. Use source when the values are wanted and mold when only the geometry is needed, because source performs a copy.

a3.f90
program a3
    implicit none
    integer :: fixed(4)
    integer, allocatable :: a(:), b(:)

    fixed = [2, 4, 6, 8]
    allocate(a, source=fixed)
    allocate(b, mold=fixed)
    b = 0

    print *, 'a:', a
    print *, 'shape of b:', shape(b)
    print *, 'b:', b
end program a3

The array a receives both the shape and the values of fixed, so it prints 2, 4, 6 and 8. The array b gets the same shape of four elements, but its values are whatever the memory contained, which is why the program assigns zero before printing. The shape report shows that mold did its job: four elements of the right type.

$ gfortran a3.f90 -o a3
$ ./a3
 a:           2           4           6           8
 shape of b:           4
 b:           0           0           0           0

Automatic reallocation on assignment

An allocatable array does not have to be allocated before it is assigned. An intrinsic assignment to an unallocated allocatable allocates it with the shape of the right-hand side, and if it is already allocated with a different shape, the runtime reallocates it. Assigning a section of the array to the array itself works too, because the right-hand side is evaluated before the assignment. The array is first given a zero-sized allocation here, so that its descriptor is initialised; GNU Fortran otherwise warns that the descriptor may be used uninitialized when the assignment allocates it.

a4.f90
program a4
    implicit none
    integer, allocatable :: a(:)

    allocate(a(0))
    a = [1, 2, 3]
    print *, 'size:', size(a), 'values:', a
    a = [a, 4, 5]
    print *, 'size:', size(a), 'values:', a
    a = a(2:4)
    print *, 'size:', size(a), 'values:', a
end program a4

The first assignment grows the empty array to three elements, and the second builds a new five-element constructor from the old values and the two new ones. The third assignment takes a section of the array, evaluates it, and only then reallocates the left side to a length of three. Each line prints the new size and contents, and no explicit allocate follows the first one.

$ gfortran a4.f90 -o a4
$ ./a4
 size:           3 values:           1           2           3
 size:           5 values:           1           2           3           4           5
 size:           3 values:           2           3           4

Growing an array in a loop

Appending to an allocatable array with a = [a, item] is the simplest way to collect an unknown number of values. Every iteration builds a new array and copies the old elements, so the cost grows with the square of the final length. That is fine for a few thousand elements and wasteful for millions; in the second case, allocate a generous size, fill it with a counter, and copy the used part at the end.

a5.f90
program a5
    implicit none
    integer, allocatable :: evens(:)
    integer :: i

    allocate(evens(0))
    do i = 1, 10
        if (mod(i, 2) == 0) then
            evens = [evens, i]
        end if
    end do
    print *, 'count:', size(evens)
    print *, evens
end program a5

The loop keeps the even numbers from 1 to 10, appending one at a time, and the final array has five elements. The zero-sized allocation before the loop gives the constructor something to extend; appending to an array that is not allocated at all would create a new one on the first iteration, which works but triggers the compiler warning described above. The output shows the count and then the collected values.

$ gfortran a5.f90 -o a5
$ ./a5
 count:           5
           2           4           6           8          10

Moving an allocation

move_alloc(from, to) transfers the storage of one allocatable array to another and leaves the source unallocated. Nothing is copied: only the descriptor moves, which makes the call cheap even for a large array. It is the standard way to return an array from a procedure without a copy, and it also replaces the contents of the destination, deallocating it first if necessary.

a6.f90
program a6
    implicit none
    integer, allocatable :: a(:), b(:)

    allocate(a(3))
    a = [1, 2, 3]
    print *, 'before: a allocated', allocated(a), ' b allocated', allocated(b)
    call move_alloc(a, b)
    print *, 'after:  a allocated', allocated(a), ' b allocated', allocated(b)
    print *, 'b:', b
end program a6

Before the call only a is allocated. Afterwards b owns the three values and a is unallocated, so the allocation status of the two variables has been swapped. The values are still 1, 2 and 3, and no element was copied. move_alloc is a subroutine, so it is called with call, and both arguments must be allocatable.

$ gfortran a6.f90 -o a6
$ ./a6
 before: a allocated T  b allocated F
 after:  a allocated F  b allocated T
 b:           1           2           3

Allocatable function results

A function may return an allocatable array, which frees the caller from knowing the result size in advance. The result variable is declared allocatable in the function and allocated inside the body, and the caller assigns the call to an allocatable array of its own; the assignment takes care of the shape. This pattern keeps the size calculation in the procedure that knows the answer.

a7.f90
program a7
    implicit none
    integer, allocatable :: squares(:)

    allocate(squares(0))
    squares = first_squares(6)
    print *, squares
contains
    function first_squares(n) result(r)
        integer, intent(in) :: n
        integer, allocatable :: r(:)
        integer :: i

        allocate(r(n))
        r = [(i * i, i = 1, n)]
    end function first_squares
end program a7

The function allocates its result with the requested length and fills it with the squares of 1 to 6, so the caller receives 1, 4, 9, 16, 25 and 36. The caller's array starts as a zero-sized allocation for the reason given earlier, and the assignment reallocates it to the six elements of the result. The compiler may move the function result into the caller's array instead of copying it.

$ gfortran a7.f90 -o a7
$ ./a7
           1           4           9          16          25          36

Zero-sized arrays

An allocation of zero elements is legal and produces an array that is allocated but has no elements. It is a useful representation of an empty result: size is zero, sum is zero, and a loop over the array body never runs, so no special case is needed for the empty input. allocated is true, which distinguishes "allocated but empty" from "never allocated".

a8.f90
program a8
    implicit none
    integer, allocatable :: a(:)

    allocate(a(0))
    print *, 'size:', size(a)
    print *, 'sum: ', sum(a)
    print *, 'allocated:', allocated(a)
end program a8

The array has no elements, so the size is 0 and the sum is 0, yet it counts as allocated. Functions that build a result from a mask, such as pack, return a zero-sized array when nothing matches, and code that sums or loops over the result needs no guard. The distinction matters when an unallocated result would be an error but an empty one is a valid answer.

$ gfortran a8.f90 -o a8
$ ./a8
 size:           0
 sum:            0
 allocated: T

Next steps

The next article, Fortran array arguments, shows how allocatable and fixed arrays are passed to procedures, and how the shape travels with an assumed-shape argument. The array intrinsics article uses the functions that often produce these arrays, and the arrays article covers the fixed-size declarations that allocatable arrays extend. A later article on derived types shows allocatable components inside a type.

Source

Fortran 2018 standard (J3/18-007r1)

GNU Fortran: ALLOCATED

GNU Fortran: SUM

In this article we have allocated and deallocated arrays, handled allocation failures with stat and errmsg, copied arrays with source and mold, relied on automatic reallocation in assignments, grown an array in a loop, moved an allocation without copying, returned an allocatable array from a function, and used a zero-sized array as an empty result.

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.