Allocatable Arrays and Dynamic Memory
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Fortran 2023 (ISO/IEC 1539-1:2023, J3/23-007r1) and was checked on 2026-10-18. Every
example was compiled warning-free with gfortran -std=f2023 -Wall -Wextra -pedantic -fimplicit-none -fcheck=all
(GNU Fortran 16.2) and run, and every example was also built and run with LLVM Flang 23; the output shown is real
output, and where the two compilers differ the difference is shown. Examples that are deliberately wrong, or that use a
feature a compiler rejects, are labelled as such and show the real diagnostics. The standard defines dynamic
association in 9.7 (allocate in 9.7.1, nullify in 9.7.2, deallocate in 9.7.3, stat= in 9.7.4 and errmsg= in
9.7.5), the allocatable attribute in 8.5.3, deferred-shape arrays in 8.5.8.4, allocation in intrinsic assignment in
10.2.1.3, allocatable dummy arguments in 15.5.2.7, and the intrinsic procedures allocated and move_alloc in 16.9.13 and
16.9.147.
An allocatable variable is a variable whose storage is requested during execution and returned by the language itself,
not by the programmer’s discipline. That one property replaces most of what a C programmer does with malloc, realloc
and free, and most of what a Fortran 77 programmer did with fixed upper bounds and work arrays passed down from the main
program. In the running example, heatflow, the temperature field u(:,:) of grid_t is an allocatable component whose
size is chosen from the input file, the Jacobi solver keeps an allocatable work array, and make_solver returns an
allocatable polymorphic scalar. This page covers the attribute, the statements and intrinsics that act on it, the six
situations in which the language changes an allocation status without a statement, the interaction with procedures and
derived types, the choice between automatic arrays, allocatable arrays and pointers, and the diagnostics a compiler
gives when the rules are broken. The shape and indexing rules of the arrays themselves are on
Arrays Fundamentals; passing arrays to procedures is on
Array Arguments: Assumed Shape, Size and Rank.
The allocatable attribute
A variable with the allocatable attribute is "a variable for which space is allocated during execution" (8.5.3). An
allocatable array is a deferred-shape array (8.5.8.4): its rank is fixed in the declaration, written with one colon per
dimension or, in Fortran 2023, with a rank clause (see The rank clause (Fortran 2023)), and its bounds are not
part of the declaration. The bounds, the extents and, for a deferred-length character or a parameterized derived type, the
length type parameters are established by the allocate statement or by an assignment, and can be different the next time.
real(dp), allocatable :: u(:, :) ! rank-2 array: shape unknown until allocated
real(dp), allocatable :: t0 ! allocatable scalar
character(:), allocatable :: label ! deferred-length character scalar
type(grid_t), allocatable :: grids(:) ! array of a derived type
class(solver_t), allocatable :: s ! polymorphic scalar: dynamic type chosen at allocation
Only variables and components can have the attribute. The result of referencing a function whose result variable is
allocatable is a value that is not itself allocatable (note to 8.5.3). The attribute can be given in the type declaration
statement, as above, or by a separate allocatable statement (8.5.3 and 8.6.2).
| Where it can appear | Example | What the language does for you |
|---|---|---|
Local variable or module variable (array) |
|
Unallocated at the start of every invocation (8.5.3 and 9.7.1.3); deallocated on return unless it has the |
Scalar |
|
The same rules as an array. Useful for large objects, polymorphic scalars and to express "absent" (see Unallocated actual arguments and optional dummy arguments). |
Component of a derived type |
|
Unallocated in a new object; deallocated when the object is; copied element by element (deep copy) by intrinsic assignment (10.2.1.3). |
Dummy argument |
|
Lets the callee allocate, reallocate and deallocate the caller’s variable. Requires an explicit interface (15.4.2.2). |
Function result |
|
The caller needs no size in advance. Requires an explicit interface (15.4.2.2). |
Deferred-length character |
|
The length is whatever the last allocation or assignment gave it. |
Polymorphic variable |
|
The dynamic type is set by |
Coarray |
|
Allocation and deallocation synchronize all images of the current team (9.7.1.2). See Coarrays, Images and Synchronization. |
Named constants, pointer entities, entities in a common block (C8123) and automatic arrays cannot be allocatable.
Whether an allocatable variable is a good choice, compared with an automatic array or a pointer, is the subject of
Automatic arrays, stack storage and the heap and Allocatable or pointer.
The lifecycle of an allocatable variable
At any time an allocatable variable has one of two allocation statuses (9.7.1.3): allocated or unallocated. The
function allocated(x) (16.9.13) is true in the first case and false in the second. An unallocated variable has no
shape and no value, so it "shall not be referenced or defined" (9.7.1.3 and 8.5.8.4); the only things a program may do
with it are to allocate it, to ask whether it is allocated, to pass it to an allocatable dummy argument or to an
optional dummy argument that will then be absent, and to give it a value by intrinsic assignment (which allocates it).
The diagram below shows every way the status changes. Every transition except the allocate and deallocate statements
is something the language does on the programmer’s behalf, and each one is covered in its own section below.
The initial state is unallocated "at the beginning of execution of a program" and at the beginning of each invocation of
a procedure for an unsaved local variable, and at the beginning of each execution of a construct for an unsaved local
variable of the construct (9.7.1.3). The Error state is reached only when no stat= specifier is present; with stat=, the failing statement leaves the
status as it was and stores a nonzero code (9.7.4). The Undefined state stands for referencing or defining an unallocated
variable: that is not an "error condition" the processor must detect, it is simply not allowed, and what happens is up to
the compiler and the checking options (see Runtime errors and diagnostics).
The table lists each transition with the clause that governs it.
| Event | Status before | Status after | Clause |
|---|---|---|---|
Start of the program, or entry to a procedure or |
(none) |
unallocated |
9.7.1.3 |
|
unallocated |
allocated |
9.7.1 |
|
allocated |
error condition |
9.7.1.3 |
|
allocated |
unallocated |
9.7.3 |
|
unallocated |
error condition |
9.7.3.2 |
Intrinsic assignment |
unallocated, or allocated with a different shape or length |
allocated with the shape and length of |
10.2.1.3 |
|
allocated |
unallocated (deallocated automatically) |
9.7.3.2 |
Invocation with an allocated actual argument for an |
allocated |
unallocated (deallocated on entry) |
9.7.3.2, 15.5.2.7 |
|
|
|
16.9.147 |
Intrinsic assignment to a variable of derived type |
components allocated |
allocatable components deallocated first, then reallocated by the assignment |
9.7.3.2, 10.2.1.3 |
Deallocation of a variable of derived type |
components allocated |
every allocated allocatable component deallocated |
9.7.3.2 |
The allocate statement
The allocate statement (R929, 9.7.1) creates allocatable variables and pointer targets:
allocate ([type-spec ::] allocation-list [, alloc-opt-list])
! allocation is allocate-object [(allocate-shape-spec-list)] [lbracket allocate-coarray-spec rbracket]
! allocate-shape-spec is [lower-bound-expr :] upper-bound-expr
! alloc-opt is one of stat=stat-variable errmsg=errmsg-variable source=source-expr mold=source-expr
Rules that matter in practice (9.7.1.1 and 9.7.1.2):
-
Each allocate-object is an allocatable variable or a data pointer (C936). It may be a component, as in
allocate (g%u(nx, ny)), but not a coindexed object (C955). -
An array needs its bounds, either as an
allocate-shape-spec-listor from the shape of asource=expression of the same rank (C943). The bounds expressions are evaluated once, and later changes to the variables in them do not change the array. A missing lower bound is 1. If an upper bound is less than the lower bound, the extent is zero and the array has zero size, but it is allocated. -
Several variables may be allocated in one statement.
allocate (x(n), b(-3:m, 0:9), stat=ierr)is the form shown in the standard. The order in which they are allocated is not specified, and an allocate-object or a bound must not depend on another allocate-object, or onstatorerrmsg, in the same statement. -
No
alloc-optmay appear twice (C948), and at most one ofsource=and a type-spec may appear (C949). -
Allocating a variable that is already allocated is an error condition. This is the opposite of what
mallocdoes and is the usual first runtime error a new Fortran programmer meets. -
A scalar allocatable has no shape specification:
allocate (t0)(C944). -
The content of a new allocation is undefined, except for the default initialization of a derived type or
source=. Only allocation and assignment, neverallocatealone, give an array values.
The first example walks an array through its states and shows the bounds, the zero-size case and an allocatable scalar.
program allocation_basics
use, intrinsic :: iso_fortran_env, only: real64, int32
implicit none
integer, parameter :: dp = real64
real(dp), allocatable :: u(:, :) ! deferred-shape rank-2 array, unallocated at start
real(dp), allocatable :: t0 ! allocatable scalar
integer(int32) :: nx, ny
nx = 5_int32
ny = 4_int32
print '(a, l1)', 'allocated(u) at start: ', allocated(u)
allocate (u(nx, ny)) ! bounds 1:nx, 1:ny
u = 0.0_dp
print '(a, l1, a, 2i3, a, 2i3, a, 2i3)', 'allocated(u) after allocate: ', allocated(u), &
' shape=', shape(u), ' lbound=', lbound(u), ' ubound=', ubound(u)
deallocate (u)
print '(a, l1)', 'allocated(u) after deallocate: ', allocated(u)
allocate (u(0:nx - 1, -1:ny - 2)) ! explicit lower bounds
print '(a, 2i3, a, 2i3)', 'lbound=', lbound(u), ' ubound=', ubound(u)
deallocate (u)
allocate (u(3, 0)) ! upper bound < lower bound: zero-sized but allocated
print '(a, l1, a, i0, a, 2i3)', 'zero-sized: allocated=', allocated(u), ' size=', size(u), ' shape=', shape(u)
allocate (t0, source=300.0_dp) ! allocatable scalar, value from source=
print '(a, f6.1)', 't0=', t0
end program allocation_basics
Output (identical with Flang 23):
allocated(u) at start: F
allocated(u) after allocate: T shape= 5 4 lbound= 1 1 ubound= 5 4
allocated(u) after deallocate: F
lbound= 0 -1 ubound= 4 2
zero-sized: allocated=T size=0 shape= 3 0
t0= 300.0
Source: J3/23-007r1: 8.5.3 ALLOCATABLE attribute, 9.7.1 ALLOCATE statement, 9.7.3 DEALLOCATE statement and 16.9.13 ALLOCATED, Fortran Programming Language: Best Practices, Allocatable Arrays
The stat= and errmsg= specifiers
Without stat=, any error condition during allocate or deallocate initiates error termination (9.7.1.2 and 9.7.3.1),
so a failed allocation ends the program with a message. With stat=, the statement sets the integer variable to zero on
success and to a positive processor-dependent value on failure (9.7.4), and errmsg= receives an explanatory message "as
if by intrinsic assignment" (9.7.5). The set of error conditions is processor dependent: it includes at least an
allocation that cannot be satisfied, allocating an allocated variable and deallocating an unallocated one. The status of an
object whose allocation failed is "processor dependent" if other objects were also in the statement (9.7.4): each object
that was successfully allocated stays allocated, each that was not retains its previous status. Check them
individually with allocated.
Because the numbers are processor dependent, a program that wants portable behavior tests stat /= 0, never a particular
value, and stat_failed_image and stat_stopped_image from iso_fortran_env are the only standard values (9.7.4).
program allocation_errors
use, intrinsic :: iso_fortran_env, only: real64, int64
implicit none
integer, parameter :: dp = real64
real(dp), allocatable :: u(:, :)
integer :: stat
character(len=200) :: msg ! fixed length: the message is truncated or blank padded
integer(int64) :: n_big
allocate (u(10, 10), stat=stat, errmsg=msg)
print '(a, i0)', 'first allocate: stat=', stat
allocate (u(10, 10), stat=stat, errmsg=msg) ! already allocated: an error condition
print '(a, i0, 2a)', 'second allocate: stat=', stat, ' errmsg=', trim(msg)
deallocate (u, stat=stat, errmsg=msg)
print '(a, i0)', 'first deallocate: stat=', stat
deallocate (u, stat=stat, errmsg=msg) ! already unallocated: an error condition
print '(a, i0, 2a)', 'second deallocate: stat=', stat, ' errmsg=', trim(msg)
n_big = 2_int64**45 ! 2**46 elements of 8 bytes: far beyond any memory
allocate (u(n_big, 2_int64), stat=stat, errmsg=msg)
print '(a, i0, 2a)', 'huge allocate: stat=', stat, ' errmsg=', trim(msg)
print '(a, l1)', 'u allocated after the failure: ', allocated(u)
if (stat /= 0) print '(a)', 'recovering: the program is still running'
end program allocation_errors
Output with GNU Fortran 16.2:
first allocate: stat=0
second allocate: stat=5014 errmsg=Attempt to allocate an allocated object
first deallocate: stat=0
second deallocate: stat=1 errmsg=Attempt to deallocate an unallocated object
huge allocate: stat=5020 errmsg=Insufficient virtual memory
u allocated after the failure: F
recovering: the program is still running
The same program with LLVM Flang 23 gives the same structure and different numbers and texts, which is exactly why portable code must not depend on them:
first allocate: stat=0
second allocate: stat=12 errmsg=Base address is not null
first deallocate: stat=0
second deallocate: stat=11 errmsg=Base address is null
huge allocate: stat=19 errmsg=Memory allocation failed
u allocated after the failure: F
recovering: the program is still running
A failed allocate leaves the variable unallocated and the program running, which makes stat= the right tool for the one
allocation whose size depends on user input, such as the field size read from the heatflow namelist. For small
allocations whose failure is unrecoverable anyway, leaving out stat= gives the shortest code and the clearest message.
Deferred-length errmsg (Fortran 2023)
Before Fortran 2023 the errmsg= variable had to be a fixed-length character variable, so the message was truncated or
padded. Fortran 2023 allows it to be a deferred-length allocatable character, which "is allocated by the processor to the
length of the explanatory message" (9.7.5, and the list of new features in the Introduction). The same rule applies to
iomsg= (see Error Handling and Program Termination).
The next program is a deliberate test of that feature and does not work with the compilers checked here:
program deferred_errmsg
implicit none
real, allocatable :: a(:)
character(:), allocatable :: msg ! Fortran 2023: errmsg-variable may be deferred length
integer :: stat
allocate (a(3))
allocate (a(3), stat=stat, errmsg=msg) ! error condition: a is already allocated
print '(a, i0)', 'stat=', stat
if (allocated(msg)) then
print '(a, i0, 2a)', 'len(msg)=', len(msg), ' msg=', msg
else
print '(a)', 'msg was left unallocated'
end if
end program deferred_errmsg
GNU Fortran 16.2 compiles it with a warning, and the program crashes when the error condition occurs:
deferred_errmsg.f90:8:85:
8 | allocate (a(3), stat=stat, errmsg=msg) ! error condition: a is already allocated
| ^
Warning: '.msg' may be used uninitialized [-Wmaybe-uninitialized]
deferred_errmsg.f90:1:23:
1 | program deferred_errmsg
| ^
note: '.msg' was declared here
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
(backtrace omitted)
Segmentation fault
LLVM Flang 23 compiles and runs it but does not implement the rule: the message is not stored.
stat=12
msg was left unallocated
Until both implement the feature, declare errmsg with a fixed length (character(len=200)) and use trim. The crash
happens only when an error condition occurs, so a program with a deferred-length errmsg passes every test of the
successful path; test the failing path too.
Initializing an allocation: source=, mold= and a type-spec
Three forms of allocate give an allocation more than a shape:
-
source=exprallocates with the dynamic type, type parameters, shape and lower bounds ofexprand copies its value (9.7.1.2). The expression is evaluated exactly once. It must be conformable with the allocation: a scalar source is broadcast to every element of an array allocation, and an array source must have the same rank as the allocate-object. Ifexpris allocatable it must be allocated, and if it is a pointer it must be associated. -
mold=exprtakes the same type, type parameters and, for an array, the same shape and bounds, but does not copy a value; the variable used as a mold need not even be defined. It is the cheap way to say "another one like this". -
A type-spec before the list,
allocate (character(len=n) :: label), gives the type explicitly. It is required when an allocate-object has a deferred type parameter, is unlimited polymorphic or is of an abstract type, unlesssource=ormold=is present (C937). The kind type parameters must match those of the allocate-object (C940), and if the declaration gives a nondeferred length, the type-spec length must equal it, or an error condition occurs.
program source_and_mold
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer, parameter :: dp = real64
integer :: i
real(dp) :: field(0:3, 2)
real(dp), allocatable :: copy(:, :), shape_only(:, :), typed(:), zeros(:)
character(:), allocatable :: label, trimmed
character(len=12) :: name = 'heatflow'
field = reshape([real(dp) :: (real(i, dp), i=1, 8)], [4, 2])
! source=: same shape, lower bounds and values as the source expression
allocate (copy, source=field)
print '(a, 2i3, a, 2i3, a, f5.1)', 'source: lbound=', lbound(copy), ' ubound=', ubound(copy), ' copy(3,2)=', copy(3, 2)
! mold=: shape, type and type parameters only; the value is NOT copied
allocate (shape_only, mold=field)
print '(a, 2i3, a, 2i3)', 'mold: lbound=', lbound(shape_only), ' ubound=', ubound(shape_only)
! a scalar source= initializes every element of an array allocation
allocate (zeros(6), source=-1.0_dp)
print '(a, i0, a, f5.1)', 'scalar source: size=', size(zeros), ' sum=', sum(zeros)
! type-spec: required for a deferred-length character unless source= or mold= is given
allocate (character(len=len_trim(name)) :: label)
print '(a, i0)', 'after allocate: len(label)=', len(label)
label = name ! intrinsic assignment reallocates to len(name) = 12
print '(a, i0, 3a)', 'after label = name: len(label)=', len(label), ' [', label, ']'
trimmed = trim(name) ! allocated on assignment with the trimmed length
print '(a, i0, 3a)', 'trimmed: len=', len(trimmed), ' [', trimmed, ']'
allocate (real(dp) :: typed(5)) ! the type-spec form also works for non-character types
print '(a, i0)', 'typed size=', size(typed)
end program source_and_mold
Output (identical with Flang 23):
source: lbound= 0 1 ubound= 3 2 copy(3,2)= 8.0
mold: lbound= 0 1 ubound= 3 2
scalar source: size=6 sum= -6.0
after allocate: len(label)=8
after label = name: len(label)=12 [heatflow ]
trimmed: len=8 [heatflow]
typed size=5
Two details in the output are worth remembering. A source= allocation keeps the lower bounds of the source (here
0:3), while an allocation by assignment from an expression follows the same rule, as the section on automatic
reallocation below shows. And label = name changed the length of label from 8 to 12: after a type-spec allocation of a
deferred-length variable, a following intrinsic assignment reallocates it again to the length of the right-hand side, which
here includes the four trailing blanks of name. Write label = trim(name) when the trailing blanks are not data.
Source: J3/23-007r1: 9.7.1.2 Execution of an ALLOCATE statement, Fortran Programming Language: Best Practices, Allocatable Arrays
For derived types, source= copies allocatable components too (a deep copy), which makes allocate (copy, source=original)
the shortest way to clone a polymorphic object whose dynamic type is not known statically. See
Object Oriented Programming for the polymorphic cases.
Bounds given by arrays (Fortran 2023)
Fortran 2023 lets an allocate statement specify the bounds with rank-one integer arrays instead of one scalar per
dimension: allocate (a(extents)) where extents is an array of size equal to the rank, and
allocate (b(lows:highs)) with both bounds as arrays (R933, C947, and "An ALLOCATE statement can specify the bounds of an
array allocation with array expressions" in the Introduction). The point is rank-independent code, where the rank is not
known to the programmer. Neither compiler checked here accepts the feature. This program is deliberately rejected:
program bounds_exprs
implicit none
real, allocatable :: a(:, :), b(:, :)
integer :: extents(2), lows(2), highs(2)
extents = [4, 3]
lows = [0, -1]
highs = [3, 1]
allocate (a(extents)) ! Fortran 2023: array upper-bounds-expr
print '(a, 2i3, a, 2i3)', 'a lbound=', lbound(a), ' ubound=', ubound(a)
allocate (b(lows:highs)) ! lower-bounds-expr:upper-bounds-expr
print '(a, 2i3, a, 2i3)', 'b lbound=', lbound(b), ' ubound=', ubound(b)
end program bounds_exprs
GNU Fortran 16.2:
bounds_exprs.f90:9:13:
9 | allocate (a(extents)) ! Fortran 2023: array upper-bounds-expr
| 1
Error: Rank mismatch in array reference at (1) (1/2)
bounds_exprs.f90:11:14:
11 | allocate (b(lows:highs)) ! lower-bounds-expr:upper-bounds-expr
| 1
Error: Array index at (1) must be scalar
LLVM Flang 23:
error: Semantic errors in bounds_exprs.f90
bounds_exprs.f90:9:13: error: The number of shape specifications, when they appear, must match the rank of allocatable object
allocate (a(extents)) ! Fortran 2023: array upper-bounds-expr
^
bounds_exprs.f90:3:24: Declared here with rank 2
real, allocatable :: a(:, :), b(:, :)
^
bounds_exprs.f90:9:15: error: Must be a scalar value, but is a rank-1 array
allocate (a(extents)) ! Fortran 2023: array upper-bounds-expr
^^^^^^^
bounds_exprs.f90:11:13: error: The number of shape specifications, when they appear, must match the rank of allocatable object
allocate (b(lows:highs)) ! lower-bounds-expr:upper-bounds-expr
^
bounds_exprs.f90:3:33: Declared here with rank 2
real, allocatable :: a(:, :), b(:, :)
^
bounds_exprs.f90:11:15: error: Must be a scalar value, but is a rank-1 array
allocate (b(lows:highs)) ! lower-bounds-expr:upper-bounds-expr
^^^^
bounds_exprs.f90:11:20: error: Must be a scalar value, but is a rank-1 array
allocate (b(lows:highs)) ! lower-bounds-expr:upper-bounds-expr
^^^^^
Until the compilers catch up, write the scalar form allocate (a(extents(1), extents(2))) or use source= with a mold of
the right shape. The same Fortran 2023 change allows arrays in pointer bounds remapping and in subscript triplets (9.5.3.2),
described on Pointers and Targets.
The rank clause (Fortran 2023)
Fortran 2023 adds a rank clause (R829 and 8.5.17) so that a deferred-shape array can be declared as
real, allocatable, rank(2) :: u instead of real, allocatable :: u(:, :). The argument is a constant expression, so
the rank can be computed: the standard’s own example logical, rank(rank(x0)), allocatable :: x1 declares an
allocatable of the same rank as x0. The clause is only allowed for dummy arguments and for entities with the
allocatable or pointer attribute (C864).
program rank_clause
implicit none
real, allocatable, rank(2) :: u ! Fortran 2023 rank clause: deferred-shape rank-2 array
allocate (u(3, 2))
u = 1.0
print '(a, i0, a, 2i3)', 'rank=', rank(u), ' shape=', shape(u)
end program rank_clause
GNU Fortran 16.2 rejects it:
rank_clause.f90:3:8:
3 | real, allocatable, rank(2) :: u ! Fortran 2023 rank clause: deferred-shape rank-2 array
| 1
Error: Invalid character in name at (1)
rank_clause.f90:4:13:
4 | allocate (u(3, 2))
| 1
Error: Allocate-object at (1) is neither a data pointer nor an allocatable variable
rank_clause.f90:5:3:
5 | u = 1.0
| 1
Error: Symbol 'u' at (1) has no IMPLICIT type
LLVM Flang 23 accepts it:
rank=2 shape= 3 2
The deallocate statement and allocated
deallocate (x, y, stat=ierr, errmsg=msg) (R944 and 9.7.3.1) returns the storage of every listed allocatable variable and
sets its status to unallocated. For a pointer it frees the target and disassociates the pointer. The rules mirror those of
allocate:
-
Deallocating an unallocated allocatable is an error condition (9.7.3.2). With
stat=the program continues, otherwise it terminates. -
An allocatable variable shall not be deallocated if it, or any subobject of it, is argument associated with a dummy argument or construct associated with an associate name. Deallocating inside a procedure the actual argument that was passed by a non-allocatable dummy is therefore not allowed.
-
Deallocating an allocatable variable with the
targetattribute makes every pointer associated with it undefined. -
The order in which several objects in one statement are deallocated is processor dependent.
-
Deallocation of a derived-type object deallocates its allocated allocatable subobjects, after any final subroutine of the object has run (9.7.3.2).
-
A
deallocatenever needs a guard in the common case:if (allocated(x)) deallocate (x)is correct, but code that relies on automatic deallocation (the next section) rarely needs the statement at all.
allocated (16.9.13) accepts an allocatable array or scalar and returns a default logical. It is one of the inquiry
functions that an unallocated variable may be passed to. It is not defined for pointers: associated (see
Pointers and Targets) is the pointer counterpart. A common
idiom for "create on first use" is
if (.not. allocated(self%work)) allocate (self%work(nx, ny))
and Fortran 2003 and later allow the shorter form if (.not. allocated(self%work)) self%work = self%grid%u, which allocates
and copies in one statement and is what the heatflow solvers use (heatflow_jacobi.f90 and heatflow_ftcs.f90).
Automatic deallocation
An allocatable variable cannot leak, because the language deallocates it. "The effect of automatic deallocation is the same
as that of a deallocate statement without a dealloc-opt-list" (9.7.3.2), including the execution of any final subroutine
before the storage goes away (7.5.6.3). It happens in these situations:
-
When a procedure ends by
returnorend, every unsaved allocated local allocatable variable of the procedure is deallocated, except a function result and its subobjects, which keep their allocation for the caller. -
When a
blockconstruct ends, every unsaved allocated local allocatable variable of the construct is deallocated. -
After the innermost executable construct that contains a reference to a function with an allocatable result (or one with an allocated allocatable subobject) has been executed, the result is deallocated. The function result is a temporary.
-
When a procedure is invoked with an allocated allocatable actual argument for an
intent(out)allocatable dummy argument, the actual argument is deallocated on entry; the same holds for an allocated allocatable subobject of an actual argument that corresponds to anintent(out)dummy argument of any kind. -
When an intrinsic assignment is executed, any allocated allocatable subobject of the variable is deallocated before the assignment, and the allocatable variable itself is deallocated if the right-hand side has a different shape (see the next section).
-
When a variable of derived type is deallocated, its allocated allocatable subobjects are deallocated, after the final subroutine of the variable has run. The same cascade happens when a variable with such a component is itself automatically deallocated.
Variables with the save attribute keep their allocation. In the main program, in a module and in a submodule, every
variable has the save attribute implicitly (8.5.16), so an allocatable variable declared there lives until the program
ends, and at the end it is not deallocated by finalization: "entities existing immediately prior to termination are not
finalized" when a program ends with stop, error stop or end program (7.5.6.4). The operating system reclaims the
memory, but a final subroutine that writes a file or closes a handle will not run for a main-program variable. Put such
variables inside a block construct if the cleanup matters.
The program below makes each rule observable by giving the allocated object a final subroutine that prints a line when it runs.
module field_store
implicit none
private
public :: tracker, scope_demo, keep_demo, make_tracked, reset_tracked
type :: tracker ! prints a line when an object of this type is finalized
character(len=8) :: tag = '?'
contains
final :: tracker_final
end type tracker
contains
subroutine tracker_final(self)
type(tracker), intent(inout) :: self
print '(2a)', ' final subroutine runs for: ', trim(self%tag)
end subroutine tracker_final
subroutine scope_demo()
type(tracker), allocatable :: local ! unsaved local: unallocated on every call
type(tracker), allocatable, save :: kept ! saved: keeps its status between calls
print '(a, l1, a, l1)', ' on entry: allocated(local)=', allocated(local), ' allocated(kept)=', allocated(kept)
allocate (local, source=tracker('local'))
if (.not. allocated(kept)) allocate (kept, source=tracker('kept'))
print '(a)', ' leaving scope_demo'
end subroutine scope_demo
subroutine keep_demo()
block
type(tracker), allocatable :: in_block
allocate (in_block, source=tracker('in_block'))
print '(a)', ' end of the block construct'
end block
print '(a)', ' after the block'
end subroutine keep_demo
function make_tracked(tag) result(r) ! the result keeps its allocation
character(len=*), intent(in) :: tag
type(tracker), allocatable :: r
allocate (r, source=tracker(tag))
end function make_tracked
subroutine reset_tracked(t)
type(tracker), allocatable, intent(out) :: t ! an allocated actual argument is deallocated on entry
print '(a, l1)', ' inside reset_tracked: allocated(t)=', allocated(t)
end subroutine reset_tracked
end module field_store
program auto_dealloc
use field_store
implicit none
type(tracker), allocatable :: t
print '(a)', 'first call of scope_demo:'
call scope_demo()
print '(a)', 'second call of scope_demo:'
call scope_demo()
print '(a)', 'block construct:'
call keep_demo()
print '(a)', 'function result, then intent(out):'
t = make_tracked('result') ! the function result is a temporary, deallocated after the statement
print '(a, l1)', ' t allocated: ', allocated(t)
call reset_tracked(t)
print '(a)', 'end of the main program'
end program auto_dealloc
Output (identical with Flang 23):
first call of scope_demo:
on entry: allocated(local)=F allocated(kept)=F
leaving scope_demo
final subroutine runs for: local
second call of scope_demo:
on entry: allocated(local)=F allocated(kept)=T
leaving scope_demo
final subroutine runs for: local
block construct:
end of the block construct
final subroutine runs for: in_block
after the block
function result, then intent(out):
final subroutine runs for: result
t allocated: T
final subroutine runs for: result
inside reset_tracked: allocated(t)=F
end of the main program
Read the output against the rules above. local is deallocated when scope_demo returns (rule 1), while kept, which
has save, is still allocated on entry to the second call and is never finalized. in_block is deallocated at
end block (rule 2), before the statement after the construct. The function result result is finalized as soon as the
assignment statement is complete (rule 3), because t = make_tracked('result') copies it into t; the second
"result" line is the copy held by t, which intent(out) deallocates on entry to reset_tracked, so
allocated(t) is false inside the subroutine (rule 4). The final subroutine does not run for t or kept at the end of
the main program.
Source: J3/23-007r1: 9.7.3.2 Deallocation of allocatable variables, 7.5.6.3 When finalization occurs, 7.5.6.4 Entities that are not finalized and 8.5.16 SAVE attribute, Fortran Programming Language: Best Practices, Allocatable Arrays
A subtle consequence of rule 3: an allocatable function result is created and destroyed per reference. In a loop,
s = norm(make_field(n)) allocates and frees the whole field at every iteration. Hoist the call or pass a work array
when the field is large (see Performance).
Automatic (re)allocation on assignment
In Fortran 2003 and later, an intrinsic assignment x = expr to an allocatable variable can allocate or reallocate the
variable. The rules are in 10.2.1.3 and are easy to state:
-
If
xis an unallocated allocatable array,exprmust have the same rank, andxis allocated with the shape ofexpr. -
If
xis allocated andexpris an array of a different shape, a character expression with a different length, or a polymorphic value of a different dynamic type,xis deallocated and then allocated again with the shape, length and type ofexpr. -
A newly allocated
xhas, for an arrayexpr, "each lower bound equal to the corresponding element oflbound (expr)`", and the old bounds when `expris a scalar. -
If
xis allocated andexprhas the same shape, nothing is allocated: the values are copied. -
The right-hand side is evaluated completely before the left-hand side changes, so
x = [x, 99.0]is legal and reads the oldx.
program reallocation_on_assignment
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer, parameter :: dp = real64
real(dp), allocatable :: x(:), history(:)
real(dp) :: y(0:4), r
integer :: step
y = [10.0_dp, 20.0_dp, 30.0_dp, 40.0_dp, 50.0_dp]
! 1. assignment to an unallocated array allocates it with the shape of the expression
x = y(1:3)
print '(a, i0, a, i0, a, i0)', 'x = y(1:3): size=', size(x), ' lbound=', lbound(x, 1), ' ubound=', ubound(x, 1)
! 2. a different shape reallocates; the new lower bound is lbound(expr)
x = y
print '(a, i0, a, i0, a, i0)', 'x = y: size=', size(x), ' lbound=', lbound(x, 1), ' ubound=', ubound(x, 1)
x = y(2:4) ! a section always has lower bound 1
print '(a, i0, a, i0, a, i0)', 'x = y(2:4): size=', size(x), ' lbound=', lbound(x, 1), ' ubound=', ubound(x, 1)
! 3. an array constructor has lower bound 1, a scalar keeps the current shape
x = [x, 99.0_dp] ! the right-hand side is evaluated first, then x is reallocated
print '(a, i0, a, f5.1)', 'x = [x, 99.0]: size=', size(x), ' last=', x(size(x))
x = 0.0_dp
print '(a, i0, a, f5.1)', 'x = 0.0: size=', size(x), ' x(1)=', x(1)
! 4. the "growing history" idiom: simple, but every step copies the whole array
allocate (history(0))
do step = 1, 5
r = 1.0_dp/real(step, dp)
history = [history, r]
end do
print '(a, i0, a, f6.4)', 'history: size=', size(history), ' last=', history(size(history))
! 5. x(:) = ... turns reallocation off: the shapes must already agree
x(:) = 1.0_dp
print '(a, i0)', 'x(:) = 1.0 keeps size ', size(x)
end program reallocation_on_assignment
Output (identical with Flang 23):
x = y(1:3): size=3 lbound=1 ubound=3
x = y: size=5 lbound=0 ubound=4
x = y(2:4): size=3 lbound=1 ubound=3
x = [x, 99.0]: size=4 last= 99.0
x = 0.0: size=4 x(1)= 0.0
history: size=5 last=0.2000
x(:) = 1.0 keeps size 4
The output shows rule 3, which surprises people. The array constructor and the section y(1:3) have lower bound 1, but the
whole array y was declared y(0:4), so x = y allocates x with lower bound 0. Code that subscripts x(1) afterwards
reads the second element. The portable habit is either to pass x to a procedure with an assumed-shape dummy argument
(which resets the lower bound to 1) or to assign x = y(:), since a section always has lower bound 1.
A section on the left-hand side switches reallocation off: x(:) = expr is an assignment to an existing array and the
shapes must conform. That is occasionally useful to protect against accidental reallocation and always dangerous when
x might be unallocated. This program is deliberately wrong:
! Deliberately wrong: a(:) = ... does not reallocate, so the shapes must already agree.
program section_assignment_mismatch
implicit none
real, allocatable :: a(:)
a = [1.0, 2.0, 3.0]
a(:) = [1.0, 2.0, 3.0, 4.0, 5.0]
print *, a
end program section_assignment_mismatch
GNU Fortran 16.2 with -fcheck=all:
At line 6 of file section_mismatch.f90
Fortran runtime error: Array bound mismatch for dimension 1 of array 'a' (3/5)
LLVM Flang 23:
fatal Fortran runtime error(section_mismatch.f90:6): Assign: mismatching element counts in array assignment (to 3, from 5)
Aborted
Compiler flags for reallocation
| Option | Effect |
|---|---|
|
Enable and disable reallocation on assignment. It is on by default except with |
|
Warn where the compiler may insert reallocation code for an array of intrinsic type, as a hint to rewrite hot loops. Not
part of |
|
Also warn for scalars and derived types. |
|
The same switch, documented in the Flang command line reference. Reallocation happened in every example on this page without it. |
Compiling realloc_lhs.f90 with -Wrealloc-lhs gives one warning for each of the five assignments that may reallocate; the first two
are shown.
realloc_lhs.f90:12:3:
12 | x = y(1:3)
| 1~
Warning: Code for reallocating the allocatable array at (1) will be added [-Wrealloc-lhs]
realloc_lhs.f90:16:3:
16 | x = y
| 1~
Warning: Code for reallocating the allocatable array at (1) will be added [-Wrealloc-lhs]
The warnings are advice, not errors. An assignment x = y where the shapes are known to agree costs only a comparison
of the shapes, but inside a hot loop the check can prevent vectorization, and a loop that grows an array with
history = [history, r] copies the entire array every iteration.
move_alloc: transferring an allocation
call move_alloc (from, to [, stat, errmsg]) (16.9.147) moves an allocation from one allocatable variable to another
without copying the data. On a successful call:
-
if
tois allocated it is deallocated first; -
if
fromis allocated,tobecomes allocated with the dynamic type, type parameters, bounds and value thatfromhad, andfrombecomes unallocated; -
if
fromis unallocated,toends up unallocated; -
a pointer associated with
frombecomes associated withtoiftohas thetargetattribute, and becomes undefined otherwise; -
fromandtomust have the same rank (and corank) and be type compatible, and iffromis polymorphic so must beto; they must not be the same variable unlessfromis unallocated.toisintent(out)and so is deallocated on entry in any case.
It is a simple subroutine (F2023) unless from is a coarray, and a pure one in the earlier standards, so it can be
called from pure procedures and from do concurrent. Its two best-known uses are growing an array without a second copy of the data and moving the result
of a computation into a component or an output argument.
module residual_log
use, intrinsic :: iso_fortran_env, only: real64
implicit none
private
public :: dp, log_t
integer, parameter :: dp = real64
type :: log_t ! growable list of residuals with spare capacity
real(dp), allocatable :: values(:)
integer :: n = 0
integer :: grow_count = 0
contains
procedure :: append => log_append
end type log_t
contains
subroutine log_append(self, r)
class(log_t), intent(inout) :: self
real(dp), intent(in) :: r
real(dp), allocatable :: bigger(:)
if (.not. allocated(self%values)) allocate (self%values(4))
if (self%n == size(self%values)) then
allocate (bigger(2*size(self%values)))
bigger(1:self%n) = self%values(1:self%n)
call move_alloc(from=bigger, to=self%values) ! hands the new block over without a second copy
self%grow_count = self%grow_count + 1
end if
self%n = self%n + 1
self%values(self%n) = r
end subroutine log_append
end module residual_log
program move_alloc_demo
use residual_log
implicit none
type(log_t) :: history
real(dp), allocatable, target :: a(:), b(:)
real(dp), pointer :: p(:)
integer :: k, stat
character(len=100) :: msg
do k = 1, 100
call history%append(1.0_dp/real(k, dp))
end do
print '(a, i0, a, i0, a, i0)', 'n=', history%n, ' capacity=', size(history%values), ' reallocations=', history%grow_count
! what move_alloc does to the two variables
allocate (a(0:2))
a = [1.0_dp, 2.0_dp, 3.0_dp]
allocate (b(10))
p => a
call move_alloc(a, b, stat=stat, errmsg=msg)
print '(a, l1, a, l1, a, i0)', 'a allocated=', allocated(a), ' b allocated=', allocated(b), ' stat=', stat
print '(a, 2i3, a, 3f5.1)', 'b took the bounds of a: lbound/ubound=', lbound(b, 1), ubound(b, 1), ' values', b
print '(a, l1, a, f5.1)', 'associated(p, b)=', associated(p, b), ' p(0)=', p(0) ! b has TARGET, so p follows the move
! moving from an unallocated variable leaves the destination unallocated
call move_alloc(a, b)
print '(a, l1)', 'after moving the unallocated a: allocated(b)=', allocated(b)
end program move_alloc_demo
Output (identical with Flang 23):
n=100 capacity=128 reallocations=5
a allocated=F b allocated=T stat=0
b took the bounds of a: lbound/ubound= 0 2 values 1.0 2.0 3.0
associated(p, b)=T p(0)= 1.0
after moving the unallocated a: allocated(b)=F
The reallocation count is the case for geometric growth. Appending 100 values to history with history = [history, r]
would copy the array 100 times with every copy larger than the last, in total about 5,000 elements; the list with capacity
doubling does five reallocations, each of which copies the used elements once (bigger(1:n) = values(1:n)) and then
move_alloc gives the new block to the component with no further copy. Even this version copies once per growth; the
standard offers no realloc that extends in place.
move_alloc makes the bounds of to equal to the bounds of from, as the second line of output shows (lbound 0),
and the pointer p stays associated with the data because b has the target attribute. Without target on to, p
would become undefined and any use of it would be an error. The compilers checked here do not diagnose that.
The optional stat and errmsg arguments are part of the Fortran 2023 text (they were not in Fortran 2008). They exist
chiefly for coarray arguments, where a stopped or failed image is an error condition (stat_stopped_image and
stat_failed_image). GNU Fortran 16.2 accepts them as keywords
(stat=stat, errmsg=msg), as the example does, but its manual still lists only the arguments from and to, and in the
test for this page a positional call move_alloc (a, b, stat, msg) crashed the compiler with an internal compiler error;
LLVM Flang 23 accepted both forms. Use the keywords, or leave the arguments out.
Allocatable dummy arguments, function results and components
Allocatable dummy arguments
A dummy argument with the allocatable attribute is associated with an actual argument that is itself allocatable
(15.5.2.7). The two must have the same type, rank and corank, the same declared type (and both polymorphic or neither),
and an explicit interface is required (15.4.2.2), which in practice means that the callee lives in a module. The actual
argument may be unallocated, and the callee may allocate, reallocate, deallocate and test it; because the dummy argument is
the actual argument, the caller sees every change. The intent decides what is allowed:
| Intent | Meaning for an allocatable dummy argument |
|---|---|
|
Neither the allocation status nor the value can be changed. The callee may test |
|
An actual argument that is allocated is deallocated on entry (9.7.3.2 and 15.5.2.7), so the callee always starts with an
unallocated variable. Allocatable components of an |
|
The callee may use the current value, reallocate it or deallocate it. An unallocated actual argument is passed unallocated. |
none |
Permitted, but the program loses the information about what the callee does. Always give an intent. |
If the dummy argument has the target attribute and the actual argument does too, pointers associated with the actual
argument remain associated with the dummy argument for the duration of the call (15.5.2.7). Without it, such pointers do
not become associated with the dummy argument.
Allocatable function results
A function can return an allocatable variable, in which case the size need not be known by the caller. The caller assigns the result to an allocatable variable, which is allocated by the assignment (previous section), or uses it directly in an expression. The result is deallocated after the innermost executable construct that contains the reference. The result variable is a local variable of the function, but the usual rule that a local allocatable is deallocated on return does not apply to it.
Allocatable components
A component with the allocatable attribute is unallocated in a newly created object and is deallocated when the object is
deallocated. Intrinsic assignment of a derived-type value copies the components: for an allocatable component the target
is deallocated first, then reallocated with the shape of the source component, and the values are copied (10.2.1.3), a
deep copy. A pointer component, by contrast, would be copied as a pointer and the two objects would share the target.
That is the main practical argument for allocatable components.
The next example puts these three in one module: a function result (linspace), an intent(inout) allocatable dummy
argument that keeps its contents when the shape does not change (ensure_field) and an allocatable component of
field_t, filled with source= and deep-copied by assignment.
module heatflow_fields
use, intrinsic :: iso_fortran_env, only: real64
implicit none
private
public :: dp, linspace, ensure_field, field_t, make_field
integer, parameter :: dp = real64
type :: field_t
integer :: nx = 0, ny = 0
real(dp), allocatable :: u(:, :) ! allocatable component
end type field_t
contains
! Allocatable function result: the caller does not need to know the size in advance.
function linspace(a, b, n) result(x)
real(dp), intent(in) :: a, b
integer, intent(in) :: n
real(dp), allocatable :: x(:)
integer :: i
allocate (x(n))
do i = 1, n
x(i) = a + (b - a)*real(i - 1, dp)/real(n - 1, dp)
end do
end function linspace
! Allocatable dummy, intent(inout): the callee may allocate, reallocate or deallocate the actual argument.
subroutine ensure_field(u, nx, ny)
real(dp), allocatable, intent(inout) :: u(:, :)
integer, intent(in) :: nx, ny
if (allocated(u)) then
if (all(shape(u) == [nx, ny])) return ! already the right shape: keep the data
deallocate (u)
end if
allocate (u(nx, ny), source=0.0_dp)
end subroutine ensure_field
function make_field(nx, ny, value) result(f)
integer, intent(in) :: nx, ny
real(dp), intent(in) :: value
type(field_t) :: f
f%nx = nx
f%ny = ny
allocate (f%u(nx, ny), source=value)
end function make_field
end module heatflow_fields
program dummies_results
use heatflow_fields
implicit none
real(dp), allocatable :: u(:, :), x(:)
type(field_t) :: f1, f2
x = linspace(0.0_dp, 1.0_dp, 5) ! x is allocated by the assignment of the result
print '(a, i0, a, 5f5.2)', 'linspace: size=', size(x), ' x=', x
call ensure_field(u, 3, 2) ! u is unallocated here, which is allowed
u(2, 2) = 5.0_dp
call ensure_field(u, 3, 2) ! same shape: contents kept
print '(a, 2i3, a, f4.1)', 'same shape: shape=', shape(u), ' u(2,2)=', u(2, 2)
call ensure_field(u, 4, 4) ! different shape: reallocated, zero filled
print '(a, 2i3, a, f4.1)', 'new shape: shape=', shape(u), ' u(2,2)=', u(2, 2)
f1 = make_field(3, 3, 1.0_dp)
f2 = f1 ! intrinsic assignment copies the allocatable component (deep copy)
f2%u(1, 1) = 99.0_dp
print '(a, f5.1, a, f5.1)', 'deep copy: f1%u(1,1)=', f1%u(1, 1), ' f2%u(1,1)=', f2%u(1, 1)
end program dummies_results
Output (identical with Flang 23):
linspace: size=5 x= 0.00 0.25 0.50 0.75 1.00
same shape: shape= 3 2 u(2,2)= 5.0
new shape: shape= 4 4 u(2,2)= 0.0
deep copy: f1%u(1,1)= 1.0 f2%u(1,1)= 99.0
x = linspace(...) allocated x, ensure_field was called with an unallocated u, and the assignment f2 = f1 copied
the 3 by 3 array, so changing f2%u(1, 1) leaves f1%u(1, 1) alone. The same code with a pointer component would have
printed 99.0 twice.
Unallocated actual arguments and optional dummy arguments
Apart from the inquiry functions such as allocated, an unallocated allocatable actual argument can be passed in only two
ways: to an allocatable dummy argument, or to an optional dummy argument that is not allocatable or pointer. In the second case the dummy argument is simply not
present (15.5.2.13): "has the ALLOCATABLE attribute and is not allocated" makes the corresponding dummy argument absent.
This is a deliberately useful rule, because an optional argument can then be controlled by whether a variable has been
allocated:
module heatflow_masks
use, intrinsic :: iso_fortran_env, only: real64
implicit none
private
public :: dp, mean_temperature
integer, parameter :: dp = real64
contains
function mean_temperature(u, weights) result(m)
real(dp), intent(in) :: u(:)
real(dp), intent(in), optional :: weights(:) ! not allocatable, but may be associated with an allocatable
real(dp) :: m
if (present(weights)) then
m = sum(u*weights)/sum(weights)
else
m = sum(u)/real(size(u), dp)
end if
end function mean_temperature
end module heatflow_masks
program optional_alloc
use heatflow_masks
implicit none
real(dp) :: u(4) = [1.0_dp, 2.0_dp, 3.0_dp, 4.0_dp]
real(dp), allocatable :: w(:)
! An unallocated allocatable actual argument is a legal way to say "argument absent"
print '(a, f6.3)', 'w unallocated: mean=', mean_temperature(u, w)
w = [4.0_dp, 3.0_dp, 2.0_dp, 1.0_dp]
print '(a, f6.3)', 'w allocated: mean=', mean_temperature(u, w)
end program optional_alloc
Output (identical with Flang 23):
w unallocated: mean= 2.500
w allocated: mean= 2.000
Passing the unallocated array to an ordinary (non-optional) assumed-shape dummy argument is not allowed, and the compilers checked here do not diagnose it. This program is deliberately wrong:
! Deliberately wrong: an unallocated array passed to a non-allocatable dummy argument.
module m
implicit none
contains
subroutine total(v, s)
real, intent(in) :: v(:)
real, intent(out) :: s
s = sum(v)
end subroutine total
end module m
program unallocated_actual
use m
implicit none
real, allocatable :: a(:)
real :: s
call total(a, s)
print *, s
end program unallocated_actual
GNU Fortran 16.2 crashes inside the callee, even with -fcheck=all:
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
(backtrace omitted)
Segmentation fault
LLVM Flang 23 prints a meaningless 0. for the sum of an array that does not exist. Both are the usual form of undefined
behavior: a crash if you are lucky.
Recursive allocatable components and polymorphic allocatables
Since Fortran 2008 a derived type may have an allocatable component of its own type, which turns allocatable components into
a safe linked data structure with automatic cleanup: deallocating the head deallocates the whole chain, and assignment
copies the whole chain. The example builds a probe list with move_alloc (it hands the old list to the new node’s next
without copying), walks it by recursion, deep-copies it and allocates an unlimited polymorphic scalar.
module heatflow_probes
use, intrinsic :: iso_fortran_env, only: real64
implicit none
private
public :: dp, probe_list, push, show
integer, parameter :: dp = real64
type :: probe_node ! recursive allocatable component (Fortran 2008)
real(dp) :: temperature = 0.0_dp
type(probe_node), allocatable :: next
end type probe_node
type :: probe_list
type(probe_node), allocatable :: head
integer :: count = 0
end type probe_list
contains
subroutine push(list, t)
type(probe_list), intent(inout) :: list
real(dp), intent(in) :: t
type(probe_node), allocatable :: new_head
allocate (new_head)
new_head%temperature = t
call move_alloc(list%head, new_head%next) ! the old list becomes the tail
call move_alloc(new_head, list%head)
list%count = list%count + 1
end subroutine push
subroutine show(list)
type(probe_list), intent(in) :: list
write (*, '(a)', advance='no') 'list:'
if (allocated(list%head)) call show_node(list%head)
write (*, '(a)') ''
end subroutine show
recursive subroutine show_node(node) ! walks the chain by recursion: no pointers needed
type(probe_node), intent(in) :: node
write (*, '(f6.1)', advance='no') node%temperature
if (allocated(node%next)) call show_node(node%next)
end subroutine show_node
end module heatflow_probes
program recursive_types
use heatflow_probes
implicit none
type(probe_list) :: probes
type(probe_list) :: copy
class(*), allocatable :: anything
integer :: k
do k = 1, 4
call push(probes, 10.0_dp*real(k, dp))
end do
call show(probes)
copy = probes ! the standard requires every node to be copied
copy%head%temperature = -1.0_dp
print '(a, f6.1, a, f6.1)', 'original head=', probes%head%temperature, ' copy head=', copy%head%temperature
allocate (anything, source=3.5_dp) ! allocatable unlimited polymorphic scalar
select type (anything)
type is (real(dp))
print '(a, f4.1)', 'dynamic type real(dp), value ', anything
end select
end program recursive_types
Output (identical with Flang 23):
list: 40.0 30.0 20.0 10.0
original head= 40.0 copy head= -1.0
dynamic type real(dp), value 3.5
Two caveats go with this design. First, deallocating the head deallocates the chain recursively. Building the same list
with 1,000,000 nodes inside a subroutine (the module is the one above, the program only calls build (1000000), where
build pushes the nodes and returns) gave count=1000000 and then Segmentation fault on return from build with GNU
Fortran 16.2 (at -O0 and -O2), and LLVM Flang 23 crashed too (before it printed the count), with the default 8 MB stack. The cleanup of a long chain needs
stack proportional to its length, so for long sequences use an array that grows with move_alloc. Second, traversing the
chain needs either recursion, as here, or a pointer that is associated with each node in turn (the pointer is a view,
owns nothing, and needs the target attribute on the head). Third, the standard requires the assignment copy = probes
to copy every node (10.2.1.3), but this program changes only the head of the copy, so it does not test the deeper nodes. GNU
Fortran 16.2 gets them wrong: from the third node on, the copy shares storage with the original, as
Derived types
shows. Test deep copies of recursive chains on every compiler you use.
Polymorphic allocatables are the third use. In heatflow, make_solver returns class(solver_t), allocatable :: s and
allocates it with a type-spec chosen at run time:
allocate (jacobi_solver_t :: s) ! or: allocate (ftcs_solver_t :: s)
The dynamic type is then jacobi_solver_t, and a select type construct or a type-bound call dispatches on it.
allocate (s, source=other) clones a polymorphic object whose dynamic type is not known statically, and
allocate (s, mold=other) makes an uninitialized object of the same dynamic type. See
Object Oriented Programming and
Type-Bound Procedures, Abstract Types and Finalization.
An allocatable coarray is allocated with a coshape, allocate (c(n)[*]), and every allocate and deallocate of a
coarray, including the automatic deallocation at the end of a procedure, synchronizes all images of the current team
(9.7.1.2, 9.7.3.2). See Coarrays, Images and Synchronization.
Automatic arrays, stack storage and the heap
A local array whose bounds are not constant is an automatic data object (8.3), for example real(dp) :: work(n) where
n is a dummy argument or a module variable: its size is fixed on entry to the procedure and it exists until the procedure
returns. It is declared, not allocated, so it cannot fail gracefully (there is no stat=), it cannot be reallocated, and it
cannot have the save attribute (C814). An automatic array is the right tool for a small scratch array whose size follows
an argument. Where it lives is not specified by the standard, and the compilers disagree:
program stack_versus_heap
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer, parameter :: dp = real64
integer, parameter :: n = 50000000 ! 50 million reals of 8 bytes = 400 MB
character(len=16) :: mode
call get_command_argument(1, mode)
select case (trim(mode))
case ('automatic')
call with_automatic(n)
case ('allocatable')
call with_allocatable(n)
case default
print '(a)', 'usage: a.out automatic|allocatable'
end select
contains
subroutine with_automatic(m) ! automatic array: size is a dummy argument
integer, intent(in) :: m
real(dp) :: work(m)
work = 1.0_dp
print '(a, es10.3)', 'automatic: sum = ', sum(work)
end subroutine with_automatic
subroutine with_allocatable(m)
integer, intent(in) :: m
real(dp), allocatable :: work(:)
integer :: stat
allocate (work(m), stat=stat)
if (stat /= 0) then
print '(a)', 'allocatable: allocation failed, the program can report it and go on'
return
end if
work = 1.0_dp
print '(a, es10.3)', 'allocatable: sum = ', sum(work)
end subroutine with_allocatable
end program stack_versus_heap
Source: J3/23-007r1: 8.3 Automatic data objects, GNU Fortran manual: Options for code generation conventions
The program allocates 400 MB of reals in a subroutine, either as an automatic array or as an allocatable array. It was
compiled warning-free as usual. Run in a Linux container with the default stack limit of 8192 KiB (ulimit -s):
| Build | a.out automatic |
a.out allocatable |
|---|---|---|
|
|
|
|
|
|
|
|
|
By default GNU Fortran puts automatic arrays on the heap (a hidden malloc and free per call), so the first row works.
-fstack-arrays moves "all arrays of unknown size and array temporaries onto stack memory" and is enabled by -Ofast
(unless -fmax-stack-var-size is given). It is faster, because there is no allocator call, and it overflows the stack for a
large array; the LLVM Flang 23 build crashed on the automatic array, so there it lives on the stack (its -fstack-arrays option is
documented for array temporaries). The allocatable version never crashed, and its failure mode is the one the
program handles: allocate with stat= returns an error code and the program can report it.
Rules of thumb that follow:
-
Use an automatic array for small scratch space and an allocatable array for anything whose size is user-controlled or could be large (grids, matrices, work arrays proportional to a problem size).
-
A crash with "Segmentation fault" on entry to a procedure that has a large automatic array or array temporary is a stack overflow until proven otherwise. Raising the limit (
ulimit -s unlimited, theOMP_STACKSIZEvariable for OpenMP threads) hides the problem; switching toallocatableremoves it. -
A fixed-size local array that is larger than a threshold is a different case in GNU Fortran. It is moved from the stack to static storage, silently making the procedure non-reentrant, with a warning:
module heatflow_scratch
implicit none
contains
subroutine smooth(u)
real, intent(inout) :: u(:)
real :: scratch(100000) ! fixed-size local array of 400 000 bytes
integer :: n
n = min(size(u), size(scratch))
scratch(1:n) = u(1:n)
u(2:n - 1) = 0.5*(scratch(1:n - 2) + scratch(3:n))
end subroutine smooth
end module heatflow_scratch
program big_local
use heatflow_scratch
implicit none
real :: u(5) = [0.0, 1.0, 2.0, 3.0, 4.0]
call smooth(u)
print '(5f5.1)', u
end program big_local
GNU Fortran 16.2 compiled it with -Wall -Wextra and printed this warning (the program then ran correctly: the output is 0.0 1.0 2.0 3.0 4.0):
big_local.f90:6:13:
6 | real :: scratch(100000) ! fixed-size local array of 400 000 bytes
| 1~~~~~~~~~~~~~~
Warning: Array 'scratch' at (1) is larger than limit set by '-fmax-stack-var-size=', moved from stack to static storage. This makes the procedure unsafe when called recursively, or concurrently from multiple threads. Consider increasing the '-fmax-stack-var-size=' limit (or use '-frecursive', which implies unlimited '-fmax-stack-var-size') - or change the code to use an ALLOCATABLE array. If the variable is never accessed concurrently, this warning can be ignored, and the variable could also be declared with the SAVE attribute. [-Wsurprising]
0.0 1.0 2.0 3.0 4.0
The warning is the compiler’s reminder that two threads, or a recursive call, would share scratch. The limit is set by
-fmax-stack-var-size=n, default 65536 bytes (documented in the GNU Fortran manual); -frecursive removes the limit and
uses the stack, and -fno-automatic forces static storage for every local. The cure that the warning itself suggests is the
right one: "change the code to use an ALLOCATABLE array". LLVM Flang 23 compiled the same program without a warning.
Source: GNU Fortran manual: Options for code generation conventions (-fstack-arrays, -fmax-stack-var-size, -fno-automatic), LLVM Flang: Command line reference, Fortran Programming Language: Best Practices, Allocatable Arrays
| Storage | Declared as | Lifetime | Failure mode |
|---|---|---|---|
Static |
a module or main-program variable, a |
the whole program |
load-time size limit (executable too large) |
Automatic array |
|
until the procedure returns |
stack overflow (or heap failure that terminates the program) |
Allocatable |
|
from |
|
Pointer target |
|
until |
leak, dangling pointer |
Memory leaks
An allocatable variable cannot leak: whichever way control leaves its scope, the compiler-generated cleanup deallocates it
(the rules of automatic deallocation, above), and an allocate of an allocated variable is an error, so a block cannot be
lost by allocating over it. Pointers have none of these protections: allocate on an associated pointer "is not an error"
(9.7.1.4), creates a new target and leaves the old one unreachable if no other pointer refers to it, and no pointer is ever
deallocated automatically (a pointer target is freed only by a deallocate statement, 9.7.3.3).
The program below makes the contrast measurable. Both branches allocate 1000 reals three times in a loop; in the
allocatable branch the loop frees each block as the rules require, and the variable allocated after the loop is freed by
the automatic deallocation on return. In the pointer branch nothing is freed.
program leaks
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer, parameter :: dp = real64
character(len=16) :: which
call get_command_argument(1, which)
select case (trim(which))
case ('allocatable')
call with_allocatable()
case ('pointer')
call with_pointer()
case default
print '(a)', 'usage: a.out allocatable|pointer'
end select
contains
subroutine with_allocatable()
real(dp), allocatable :: work(:)
integer :: k
do k = 1, 3
allocate (work(1000)) ! allocating an allocated variable is an error, so every pass must free it
work = real(k, dp)
deallocate (work)
end do
allocate (work(1000)) ! never deallocated explicitly: freed automatically on return
work = 0.0_dp
end subroutine with_allocatable
subroutine with_pointer()
real(dp), pointer :: work(:)
integer :: k
do k = 1, 3
allocate (work(1000)) ! the previous block is silently lost: pointers may be re-allocated
work = real(k, dp)
end do
end subroutine with_pointer
end program leaks
The program was built with the strict options and run under Valgrind 3.24.0 in the container (valgrind --leak-check=full);
the output below is filtered to the summary lines:
$ valgrind --leak-check=full ./a.out allocatable
==12== in use at exit: 0 bytes in 0 blocks
==12== total heap usage: 22 allocs, 22 frees, 37,575 bytes allocated
==12== All heap blocks were freed -- no leaks are possible
$ valgrind --leak-check=full ./a.out pointer
==14== in use at exit: 24,000 bytes in 3 blocks
==14== total heap usage: 21 allocs, 18 frees, 29,571 bytes allocated
==14== 24,000 bytes in 3 blocks are definitely lost in loss record 1 of 1
==14== LEAK SUMMARY:
==14== definitely lost: 24,000 bytes in 3 blocks
==14== indirectly lost: 0 bytes in 0 blocks
The allocatable branch frees every block; the pointer branch loses three blocks of 8000 bytes. The other 18 blocks that appear in both runs belong to the Fortran runtime and the standard library. Valgrind is described on Testing, Debugging and Profiling.
"Cannot leak" is not "never grows". Three patterns keep memory alive that nobody needs:
-
an allocatable with
save, or declared in a module or the main program, lives until the program ends, and a work array stored in it is never freed even though it is no longer used; -
an allocatable component of a long-lived object, such as a log that is only appended to (the
historyof theheatflow_statsmodule on Modules and Submodules) grows with every call; -
a
pointerto an allocatable (which needs thetargetattribute) can dangle when the allocatable is deallocated, reallocated by assignment or moved bymove_allocwithouttargeton the new owner (9.7.3.2 and 16.9.147).
Allocatable or pointer
Both attributes give you run-time sized objects. The guidance in the fortran-lang best practices and in the usual Fortran
literature is the same: use allocatable unless you need what only a pointer can do.
| Need | Use | Reason |
|---|---|---|
A work array, a grid, a table, a result of unknown size |
|
automatic deallocation, no aliasing, contiguous by definition, and the optimizer may assume nothing else refers to it |
A growable sequence |
|
the amortized pattern above |
A component that must be copied with its owner |
|
intrinsic assignment makes a deep copy; with a |
Return an array of unknown size from a function |
|
no pointer to free, no leak when the caller ignores the result |
An optional piece of data |
|
|
A view of part of another array (a section, a row, a diagonal), or an alias with a different shape |
|
a pointer is the only way to name a section as a variable; see Pointers and Targets |
A linked structure in which several objects refer to one node, a graph, a tree with parent pointers |
|
shared ownership does not fit allocatable |
A procedure pointer, or a callback stored in a type |
procedure |
allocatable procedures do not exist |
Interoperating with C, where C owns the memory |
|
Most of the time an allocatable array is also faster: a pointer array may be non-contiguous and aliased, so the compiler
must generate conservative code (or a contiguity check) where it can assume the opposite for an allocatable.
Runtime errors and diagnostics
The common failures are shown by one program that runs one deliberately wrong case chosen from the command line:
! Deliberately wrong: each case triggers one runtime error; the case is chosen on the command line.
program allocation_runtime_errors
implicit none
real, allocatable :: a(:)
character(len=16) :: which
call get_command_argument(1, which)
select case (trim(which))
case ('reference') ! reading an unallocated array
print *, a(1)
case ('allocate-twice') ! allocate without stat= on an allocated array
allocate (a(3))
allocate (a(4))
case ('deallocate-twice') ! deallocate without stat= on an unallocated array
allocate (a(3))
deallocate (a)
deallocate (a)
case ('bounds') ! subscript beyond the allocated extent
allocate (a(3))
a(4) = 1.0
case ('whole') ! whole-array use of an unallocated array
print *, sum(a)
case default
print '(a)', 'usage: a.out reference|allocate-twice|deallocate-twice|bounds|whole'
end select
end program allocation_runtime_errors
Built with gfortran -std=f2023 -Wall -Wextra -pedantic -fimplicit-none -fcheck=all (GNU Fortran 16.2); for each argument
the first two lines of output are:
$ ./a.out reference
At line 10 of file runtime_errors.f90
Fortran runtime error: Index '1' of dimension 1 of array 'a' below lower bound of 281474053255328
$ ./a.out allocate-twice
At line 13 of file runtime_errors.f90
Fortran runtime error: Attempting to allocate already allocated variable 'a'
$ ./a.out deallocate-twice
At line 17 of file runtime_errors.f90
Fortran runtime error: Attempt to DEALLOCATE unallocated 'a'
$ ./a.out bounds
At line 20 of file runtime_errors.f90
Fortran runtime error: Index '4' of dimension 1 of array 'a' above upper bound of 3
$ ./a.out whole
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
With LLVM Flang 23 and no extra options:
$ ./a.out reference
Segmentation fault
$ ./a.out allocate-twice
(no output, exit status 0)
$ ./a.out deallocate-twice
(no output, exit status 0)
$ ./a.out bounds
(no output, exit status 0)
$ ./a.out whole
0.
The two cases that reference an unallocated array are undefined behavior (9.7.1.3 and 8.5.8.4): the first message shows
a garbage lower bound read from the descriptor, and whole printed 0.00000000 on one run and crashed on another. GNU
Fortran also warned about both at compile time ('a.dim[0].lbound' may be used uninitialized [-Wmaybe-uninitialized]),
which is the cheapest diagnostic available. Allocating an allocated variable and deallocating an unallocated one are
error conditions that the standard requires to terminate the program when there is no stat=; Flang 23 as tested here
ran on silently, so do not rely on it to find those bugs. The out-of-bounds subscript is only caught when bounds checking
is on (-fcheck=bounds, part of -fcheck=all).
| GNU Fortran option | What it checks or does for allocatables |
|---|---|
|
subscripts against the actual allocated bounds of a deferred-shape array, and string lengths in array constructors |
|
run-time checks for pointers and allocatables |
|
memory allocation, such as the hidden allocations for automatic arrays and temporaries; explicit |
|
warns at run time when a temporary array had to be created for an actual argument |
|
all of the above and more |
|
unallocated arrays that are referenced, as in the compile-time warnings above |
|
compile-time report of array temporaries |
|
assignments that may reallocate |
|
where local arrays live |
Performance
-
Allocate once, reuse.
allocateanddeallocatego to the system allocator and are slow compared with arithmetic. A time-stepping loop such as the one in heatflow allocates its work arrays before the first step; the Jacobi and FTCS solvers do it on first use (if (.not. allocated(self%work)) self%work = self%grid%u) and keep the array for all later steps. -
Do not grow one element at a time.
x = [x, v]is correct and compact, and it copies the whole array each time. Use capacity doubling withmove_alloc, as above, or compute the final size first. -
Beware of allocatable function results in loops (previous sections). Each reference allocates and frees. Prefer a subroutine with an
intent(out)argument and an array allocated once, and avoidallocateinsidedo concurrentand OpenMP regions unless each iteration really needs private storage. -
x = ymay reallocate. In hot loops writex(:) = yor use the compiler warnings above to find assignments that contain a hidden shape check and allocation. -
source=initialises.allocate (a(n), source=0.0_dp)writes every element, which is what you want for an accumulator and a waste for an array that is overwritten next. Without it the contents are undefined. -
Allocatable beats pointer for the optimizer (guidance above).
-
Large allocations are typically committed lazily on Linux:
allocateof a large array may succeed and the program may be killed when the pages are touched.stat=cannot catch that.
More on memory layout, contiguity and temporaries is on Performance and Memory Layout.
Memory that C allocated and memory that Fortran allocated must be returned by the side that allocated it: never pass the storage
of a Fortran allocatable array to C free, and never deallocate a pointer that was set by c_f_pointer from a C
malloc. The C descriptor functions CFI_allocate and CFI_deallocate exist for allocatable arrays shared with C; see
C Descriptors and ISO_Fortran_binding.
Coming from another language
| In C, C++, Python or MATLAB | In Fortran | Difference |
|---|---|---|
|
|
No |
|
|
There is no in-place extension; every growth copies. |
|
allocatable array with a count and |
No |
|
|
Sole ownership, automatic release and a deep copy on assignment are the default behavior of an allocatable. |
|
|
Aliasing and manual release. |
NumPy |
|
Same cost as |
MATLAB |
|
Same: the array is copied; MATLAB hides it. |
Java and Python object references |
|
Assignment of an allocatable copies the object, including its allocatable components; two names for one object need a pointer. |
See Fortran for Programmers of Other Languages for the full comparison.
Compiler support and diagnostics
The following was verified on 2026-10-18 with the examples on this page; the "not verified" family names mean that no compiler of that family was available.
| Feature | GNU Fortran 16.2 | LLVM Flang 23 |
|---|---|---|
|
Accepted; all examples built warning-free with |
Accepted, with different |
Reallocation on assignment, including bounds from |
Yes, on by default ( |
Yes. |
Error termination for |
Terminates with a message. |
In the test for this page the program ran on with exit status 0 and no message. |
|
Compiles with a warning; crashes with SIGSEGV when an error occurs. |
Compiles; the message is not stored and the variable stays unallocated. |
Array expressions as |
Rejected: "Rank mismatch in array reference" and "Array index must be scalar". |
Rejected: "Must be a scalar value, but is a rank-1 array". |
|
Rejected ("Invalid character in name"). |
Accepted. |
|
Accepted; the positional form |
Accepted in both forms. |
Recursive allocatable component (F2008), |
Yes. |
Yes. |
Run-time checking of allocatable references ( |
Yes; see the table of options above. |
Not available in the version tested ( |
Not verified here, because no compiler for them is installed: Intel ifx, NAG, HPE Cray and NVIDIA HPC compilers. See History, Standards and Compilers for the per-compiler support table of the other Fortran 2023 features, and Compilers and Compiler Options for the options.
Standard-version provenance
| Feature | Standard | Notes |
|---|---|---|
|
Fortran 90 |
The first form of dynamic memory in standard Fortran; |
Automatic deallocation of local allocatable arrays on return |
Fortran 95 |
In Fortran 90 their status after return was not defined, which made them unreliable in procedures. |
Allocatable dummy arguments, function results and components (the technical report ISO/IEC TR 15581) |
Fortran 2003 |
Incorporated into the standard in Fortran 2003, with allocatable scalars. |
Reallocation on assignment, |
Fortran 2003 |
|
|
Fortran 2008 |
|
Optional |
Fortran 2018 or 2023 (the Fortran 2023 text has both) |
Not in Fortran 2008. |
Arrays as bounds in |
Fortran 2023 |
None of the three works in GNU Fortran 16.2, and only the |
Allocation of an unallocated coarray or coindexed object by assignment |
Removed in Fortran 2023 |
It was permitted in Fortran 2008 (4.3.4 of the Fortran 2023 text) for scalar, nonpolymorphic coarrays without deferred type parameters. |
Fortran 2028 (draft J3/26-007r1) |
Upcoming |
Not covered by this page; see What’s New and Fortran 2028 Preview. |
Where the books are outdated
Modern Fortran Explained (Metcalf, Reid, Cohen and Bader, 3rd edition, Fortran 2023) covers allocatable variables, dummy
arguments, results and components, allocate and deallocate, reallocation on assignment and move_alloc in its
chapter 6, with the Fortran 2023 additions near the end. As a reference to the language it remains
accurate. The points where a reader has to look beyond it are:
-
Compiler status. The book describes the Fortran 2023 features as part of the language and does not say which compilers implement them. As the table above shows, the array bounds in
allocateand the allocation of a deferred-lengtherrmsgdo not work in GNU Fortran 16.2 or LLVM Flang 23, and therankclause works only in Flang. Portable code writes the scalar bounds and a fixed-length message variable. -
Compiler options and diagnostics. The book says nothing about
-frealloc-lhs,-Wrealloc-lhs,-fcheck=bounds,-fstack-arraysor-fmax-stack-var-size, about the run-time messages of allocation errors, or about the fact that "Segmentation fault" on entry to a procedure with a large automatic array is a stack overflow. They are covered above. -
Stack and heap. The book does not discuss where automatic arrays live. As the experiment above shows, the answer depends on the compiler and on its options.
-
Diagnosing leaks. The book shows that allocatables do not leak but gives no tool to find the leaks that pointers still allow; Valgrind and
-fcheck=pointerare described above. -
Appendix A. The book’s list of deprecated features is the authors' recommendation. None of what this page covers (
allocate,pointer, reallocation on assignment) is obsolescent in the standard.
The fortran-lang best-practices page on allocatable arrays is current in its advice, but its resize snippet declares
neither its work array tmp nor consistently spells initial_size, so it does not compile as printed; use the pattern above.
Common mistakes at a glance
| Mistake | Symptom | Fix |
|---|---|---|
|
"Attempting to allocate already allocated variable", or error termination |
|
|
"Attempt to DEALLOCATE unallocated", or error termination |
Test with |
Reading an unallocated array |
Garbage, "below lower bound of" a huge number, or a crash |
Allocate before use; build with |
Passing an unallocated array to a non-allocatable, non-optional dummy argument |
Crash inside the callee |
Allocate first, or make the dummy argument |
|
"Array bound mismatch" (or silent overrun without |
Write |
Assuming lower bound 1 after |
Off-by-one reads |
Remember that |
|
Trailing blanks in the new value |
|
|
Quadratic time |
Capacity doubling with |
Allocatable function result called in a loop |
Repeated allocation and deallocation |
Pass the output array as an |
Large automatic array |
"Segmentation fault" on entry to a procedure |
Use an allocatable array, or raise the stack limit as a stop-gap. |
Large fixed-size local array in GNU Fortran |
|
Use an allocatable array. |
Pointer to an allocatable without |
Undefined pointer |
Add |
Deallocating an object while one of its parts is a dummy argument |
Not allowed, no diagnostic |
Deallocate from the owner after the call. |
Relying on a particular |
Works on one compiler only |
Test |
|
Crash on GNU Fortran 16.2 |
Use a fixed-length variable. |
Counting on a final subroutine of a main-program variable |
It does not run at the end of the program |
Put the variable in a |