Fortran pointers
last modified November 14, 2026
An ordinary variable is a name for a piece of storage, and a copy of the
variable is a second piece with the same contents. A pointer is a
different kind of variable: it does not hold a value but the address of
another variable, called its target. Two pointers can name the same
target, a pointer can be moved from one target to another without copying
anything, and a pointer can be the component of a type that links one value to
the next. This article introduces the declaration, the
=> operator, associated and nullify,
array pointers with their own bounds, and the places where a pointer is the
right tool and where an allocatable array is better.
A pointer to a variable
The target must be declared with the target attribute, and the
pointer with pointer; both must have the same type. The statement
p => n is pointer assignment: it makes p
refer to n rather than copying its value. From then on
p may be used wherever n could be used, and an
assignment through the pointer changes the target. nullify breaks
the association, and associated reports whether a pointer
currently refers to anything.
program p1
implicit none
integer, target :: n
integer, pointer :: p
n = 10
p => n
print *, 'p points to', p
print *, 'associated:', associated(p)
p = 20
print *, 'n is now', n
nullify (p)
print *, 'associated after nullify:', associated(p)
print *, 'n is still', n
end program p1
The pointer prints the value 10 of its target, and after p = 20
the variable n itself is 20. The pointer is still associated at
that point; only nullify ends the association, and the value of
n survives it. A pointer that has never been associated, or one
that has been nullified, must not be read: testing it with
associated first is the safe way to use it.
$ gfortran p1.f90 -o p1 $ ./p1 p points to 10 associated: T n is now 20 associated after nullify: F n is still 20
Two pointers, one target
Several pointers may refer to the same target. This is called aliasing,
and it is the property that makes pointers powerful and dangerous at the same
time: a write through one name is visible through every other name for the same
storage, and the compiler is not allowed to assume otherwise. The second form of
associated, with two arguments, asks whether two pointers refer to
the same target.
program p2
implicit none
real, target :: value
real, pointer :: a, b
value = 1.5
a => value
b => value
print *, 'a, b:', a, b
a = 9.0
print *, 'through b:', b
print *, 'value: ', value
b => value
print *, 'same target:', associated(a, b)
end program p2
Both pointers show 1.5 at first, and the assignment through a
changes the value seen by b and by the target itself. No copy was
made anywhere, so the three names always agree. The last
associated(a, b) confirms that the two pointers still share one
target; it would be false if one of them had been pointed somewhere else.
$ gfortran p2.f90 -o p2 $ ./p2 a, b: 1.50000000 1.50000000 through b: 9.00000000 value: 9.00000000 same target: T
Array pointers and sections
A pointer may have the rank and the bounds of an array, and it may point at a
section of a larger array. The bounds are part of the pointer, not of the
target: first(1:3) => grid(1:3) gives a pointer whose elements
are first(1) to first(3), while
step2(0:3) => grid(2:8:2) takes every second element and
renumbers them from zero. A pointer may even change the rank of its target,
which reshapes the same storage without moving it.
program p3
implicit none
integer, target :: grid(9)
integer, pointer :: first(:), step2(:), square(:,:)
integer :: i
grid = [(i, i = 1, 9)]
first(1:3) => grid(1:3)
print *, 'first: ', first
step2(0:3) => grid(2:8:2)
print *, 'step2: ', step2
print *, 'bounds:', lbound(step2), ubound(step2)
square(1:3, 1:3) => grid
do i = 1, 3
print *, 'row', i, ':', square(i, :)
end do
step2(0) = 20
print *, 'grid: ', grid
end program p3
The pointer first sees 1, 2 and 3; step2 sees 2, 4, 6
and 8 and reports bounds 0 and 3. The three-by-three view fills its rows from
column-major storage, so the first row is 1, 4 and 7. Writing 20 through
step2(0) changes the second element of grid, which
proves that the view is not a copy. These views are cheap, but they are also
fragile: the target must stay alive and must not be deallocated while a pointer
still refers to it.
$ gfortran p3.f90 -o p3 $ ./p3 first: 1 2 3 step2: 2 4 6 8 bounds: 0 3 row 1 : 1 4 7 row 2 : 2 5 8 row 3 : 3 6 9 grid: 1 20 3 4 5 6 7 8 9
Pointer components
A component may be a pointer, which lets one value refer to storage owned by
another. The type below describes a window into a real array; the procedure
make_slice takes the whole array as a target dummy
argument and points the component at the requested section. Nothing is copied,
and the default initializer => null() guarantees that a fresh
window does not point anywhere until it is set.
module views
implicit none
private
public :: slice, make_slice
type :: slice
real, pointer :: data(:) => null()
end type slice
contains
subroutine make_slice(s, a, first, last)
type(slice), intent(out) :: s
real, target, intent(inout) :: a(:)
integer, intent(in) :: first, last
s%data => a(first:last)
end subroutine make_slice
end module views
program p4
use views
implicit none
real, target :: values(8)
type(slice) :: win
integer :: i
values = [(real(i), i = 1, 8)]
call make_slice(win, values, 3, 6)
print *, 'window:', win%data
win%data = 0.0
print *, 'values:', values
end program p4
The window shows values 3 to 6, and setting them to zero changes the middle of
values while the first two and the last two elements keep their
original numbers. The target belongs to the program, not to the window, so the
window must not outlive it. For a component that should own its storage
instead of borrowing it, an allocatable component is the correct
choice, and the derived types article uses one for the vertices of a polygon.
$ gfortran p4.f90 -o p4 $ ./p4 window: 3.00000000 4.00000000 5.00000000 6.00000000 values: 1.00000000 2.00000000 0.00000000 0.00000000 0.00000000 0.00000000 7.00000000 8.00000000
Redirecting pointers
Because a pointer is a separate variable, swapping two pointers swaps which
values are named without moving the values themselves. This is the cheapest
possible exchange and a good illustration of what pointer assignment does: the
integers x and y never move, only the two names
change places.
program p5
implicit none
integer, target :: x, y
integer, pointer :: p, q, t
x = 1
y = 2
p => x
q => y
print *, 'before:', p, q
t => p
p => q
q => t
print *, 'after: ', p, q
print *, 'x, y: ', x, y
end program p5
Before the swap, p names the 1 and q names the 2;
after it, p names the 2 and q the 1. The variables
themselves are untouched, so x is still 1 and y is
still 2. Reordering a collection of large objects by moving pointers is much
faster than copying the objects, which is why pointer-based containers are
common in programs that handle big records.
$ gfortran p5.f90 -o p5 $ ./p5 before: 1 2 after: 2 1 x, y: 1 2
Allocating through a pointer
A pointer can also own storage: allocate(p(4)) creates an
anonymous target and associates p with it. Such an array behaves
much like an allocatable one for reading and writing, but the lifetime rules
are different: the runtime does not free it when the pointer goes out of scope,
so the program must call deallocate itself. After a deallocation
the association status of the pointer is undefined until it is nullified.
program p6
implicit none
integer, pointer :: p(:)
integer, allocatable :: a(:)
allocate (p(4))
p = [1, 2, 3, 4]
allocate (a(4))
a = p * 10
print *, 'pointer: ', p
print *, 'allocatable:', a
deallocate (p, a)
nullify (p)
print *, 'after nullify:', associated(p)
end program p6
The pointer holds 1 to 4 and the allocatable array holds the same values
multiplied by ten. Both are released explicitly, and the nullify makes the
pointer safe to test afterwards. For a plain array that lives as long as its
scope, allocatable is the better choice: it is deallocated
automatically, it cannot alias another name, and the compiler can optimise it
more freely. Pointers are needed when the association itself is the point, as
in the next example and in the linked structures of the following article.
$ gfortran p6.f90 -o p6 $ ./p6 pointer: 1 2 3 4 allocatable: 10 20 30 40 after nullify: F
Returning a reference
A subroutine can return a pointer, which allows the caller to work with one
element of an array as if it were a separate variable. The search below takes a
pointer argument with intent(out), sets it to null first, and makes
it refer to the matching element when it finds one. The caller tests the result
with associated and either uses the reference or treats the search
as a failure.
module lookup
implicit none
private
public :: find_first
contains
subroutine find_first(a, value, pos)
integer, target, intent(inout) :: a(:)
integer, intent(in) :: value
integer, pointer, intent(out) :: pos
integer :: i
pos => null()
do i = 1, size(a)
if (a(i) == value) then
pos => a(i)
return
end if
end do
end subroutine find_first
end module lookup
program p7
use lookup
implicit none
integer, target :: data(6)
integer, pointer :: found
data = [4, 8, 15, 16, 23, 42]
call find_first(data, 15, found)
if (associated(found)) then
print *, 'found:', found
found = -15
end if
call find_first(data, 99, found)
print *, 'found 99:', associated(found)
print *, 'data:', data
end program p7
The value 15 is found at position three, and writing -15 through
the returned pointer changes the array in place. The search for 99 finds
nothing, so the pointer stays null and the program can report the miss without
any special status code. The dummy argument a is declared
target so that its elements may be associated with a pointer; a
dummy without that attribute cannot be a target.
$ gfortran p7.f90 -o p7 $ ./p7 found: 15 found 99: F data: 4 8 -15 16 23 42
Next steps
Pointers become indispensable when a type must refer to another value of its
own type. The linked lists, stacks and
queues article builds those structures with a next pointer in
every node, and the allocatable arrays
article explains the managed alternative used for ordinary arrays. The
arrays article covers the sections and the
column-major order that the reshaped view relies on.
Source
Fortran 2018 standard (J3/18-007r1)
Fortran-lang: learning resources
In this article we have pointed a pointer at a variable and changed the target through it, shared one target between two pointers, viewed an array through sections and a reshaped pointer, borrowed storage from an array through a pointer component, redirected pointers to swap names instead of values, allocated through a pointer, and returned a reference to one element of an array. The previous article, Fortran operator overloading, covers the interfaces that make a type behave like a built-in value.
Author
List all Fortran tutorials.