ZetCode

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.

p1.f90
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.

p2.f90
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.

p3.f90
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.

p4.f90
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.

p5.f90
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.

p6.f90
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.

p7.f90
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)

GNU Fortran manual

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

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.