Fortran Pointers

A Fortran pointer is a typed view of a variable rather than a raw address: it can point at an existing target or own allocated storage, and it remembers its association status. Used well, pointers build linked structures and alias data without copying; used carelessly they break the optimizer's assumptions. This lesson maps the model and the pitfalls.

Pointer vs allocatable

The first decision is which tool fits the job. An allocatable variable owns its memory — assignment copies, leaving is automatic. A pointer references memory that someone else owns or that it allocates itself — assignment rebinds. Rule of thumb: if the variable only needs dynamic sizing, use allocatable; if it must alias an existing object or build a linked graph, use pointer. Allocatable is safer and the optimizer loves it; pointer is the occasionally necessary power tool.

program pointer_basics
  implicit none
  real, target :: value = 3.5      ! target: addressable storage
  real, pointer :: p               ! p is born "disassociated"
  p => value                       ! pointer assignment: rebind, no copy
  print *, p                       ! 3.5 through the pointer
  p = 7.0                          ! writes THROUGH the pointer
  print *, value                   ! 7.0 — original variable changed
  nullify (p)                      ! p disassociated again; value alive
end program pointer_basics

Two forms of assignment

Fortran carefully distinguishes the two things other languages blur: pointer assignment (p => target) rebinds what the pointer looks at; ordinary assignment (p = value) through a pointer writes the pointed-to data. Mixing them is the number-one pointer bug: p = value when you meant a rebind silently overwrites the target instead.

When a pointer allocates its own storage, ordinary deallocate frees it, and the intrinsic associated(p) queries whether a pointer points at anything at all:

program pointer_allocate
  implicit none
  real, pointer :: p(:)
  integer :: n = 100
  allocate (p(n))                  ! the pointer owns this block
  p = 1.0                          ! fills through the pointer
  print *, associated(p), size(p)  ! T  100
  deallocate (p)
  print *, associated(p)           ! F — dangling references now averted
end program pointer_allocate

Targets, aliasing and the optimizer

A pointer may only point at variables declared target (or at pointer-allocated storage); the attribute tells the compiler the variable's address may escape. That single fact changes optimization: a subroutine receiving intent(inout) plus a pointer into the same object forces the compiler to assume the worst. The performance lesson measures exactly this cost. For now two habits protect you: keep pointer use inside small, isolated procedures, and never pass both a pointer and its target to the same call.

Linked structures

The classic self-referential type builds a linked list — each node holds data plus a pointer to the next node. Derived-type pointer components are where this model shines (and where allocatable components would deadlock):

program linked_list
  implicit none
  type :: node
    integer :: value
    type(node), pointer :: next            ! link to the next node
  end type node
  type(node), target :: first, second, third
  type(node), pointer :: cur

  first%value = 1; second%value = 2; third%value = 3
  first%next => second                     ! wire the chain by rebinding
  second%next => third
  third%next => null()                     ! terminate the chain

  cur => first
  do while (associated(cur))              ! walk until the null link
     print '(a,i0)', 'node value: ', cur%value
     cur => cur%next                      ! advance: whole traversal idiom
  end do
end program linked_list

Real HPC code uses this shape rarely — arrays and allocatable containers beat pointers for both speed and readability — but file systems, symbol tables and parser internals still need it, and when they do, pointer components plus => wiring are the whole technique.

Pointer hygiene: always initialize pointers to => null(); test associated(p) before dereferencing; pair every allocate with a deallocate or nullify; and prefer allocatable to pointer wherever ownership (rather than aliasing) is what you actually want.