More on pointers
Last updated on 2026-07-10 | Edit this page
Overview
Questions
- How are pointers associated with data?
- How can pointers be used to access arrays?
- How can pointers be associated with procedures?
Objectives
- Understand pointer association
- Understand how pointers allow us to create ‘views’ of arrays
- Understand the use of procedure pointers
In C, a (bare) pointer is simply a variable that holds an address.
(C++ has more sophisticated pointer types such as
unique_ptr etc.). In this section we look more closely at
pointers in Fortran.
Pointer assignment
We recall that a pointer variable may be undefined, unassociated (sometimes “disassociated”), or associated:
integer, pointer :: p1 ! undefined
integer, pointer :: p2 => null() ! unassociated
integer, pointer :: p3 => t ! associated with target t
The difference between undefined and unassociated is that an
undefined pointer can not have its association status queried by the
intrinsic function associated().
Pointer assignment is of the form:
pointer => target
where the target may be a variable with the target attribute, another pointer, or a function returning a pointer result. The type, type parameters and rank of both sides of the assignment must match.
Target is an array
For example, if the target is an array, we might have:
integer, target :: a(10)
integer, pointer :: p(:)
p => a(1:10:2)
then the shape and bounds are those the right-hand side. We can see
here that the pointer is not a simple object. It should be viewed as a
descriptor which holds information about what it is pointing to. Here
one could use intrinsic functions shape(),
lbound(), and ubound() to interrogate the
pointer as one would for an array. The pointer must be associated to do
so.
It is possible to specify the lower bound of a pointer array:
p(2:) => a(2:6)
in which case elements are indexed from the lower bound when using the pointer.
A multi-dimensional pointer may be “reshaped” to a lower-dimensional target. For example:
integer, parameter :: n = 3
integer, target :: storage(n*n)
integer, pointer :: matrix(:, :)
matrix(1:n, 1:n) => storage(:)
Note that both the lower and upper bounds of
matrix(1:n, 1:n) are included on the left-hand side of the
pointer assignment.
Target is a pointer
If the target of an assignment is a pointer, then subsequent changes in status of the target are not reflected in the later assignment, e.g.,
b => a
c => b
nullify(b)
leaves c associated with a.
The nullify() statement has the same effect as
assignment to null(). However, nullify() can
take a comma-separated list of pointer arguments if desired. If the
argument has been allocated via allocate(), then
nullify() will not perform deallocation: use
deallocate().
Exercise: (5 minutes)
Pointer shapes and bounds
Write a program which initialises an integer array a(10)
and assigns the elements the values 1-10. Associate a pointer with the
even-numbered elements of a(2:10) (as above). Print out the
values returned by the functions lbound(),
ubound() and size() when applied to the
pointer. Check the value associated with the fourth element of the
pointer is eight.
A bare outline is provided in example1.f90.
program example1
implicit none
integer, target :: a(10)
integer, pointer :: p(:)
integer :: i
p => a(2:10:2)
a = [ (i, i = 1, 10) ]
! print pointer lower bound lbound()
print *, "Lower bound: ", lbound(p) ! => 1
! print pointer upper bound ubound()
print *, "Upper bound: ", ubound(p) ! => 5
! check size and elements
print *, "Size: ", size(p) ! => 5
print *, "p(4) = ", p(4) ! => 8
end program example1
Pointers as arguments
A dummy argument may have the pointer attribute. If the intent of the
dummy argument is intent(inout) or
intent(out), the relevant actual argument must also be a
pointer.
A pointer actual argument can correspond to a non-pointer dummy argument, in which case the pointer actual argument must be associated with a suitable target.
Arguments must be distinct
C programming has the idea of restrict for pointer
arguments to functions. The restrict qualifier is a
guarantee to the compiler that there will be no overlap in the memory
accessed via different pointers: all the relevant memory locations are
distinct. (If this is not the case then the situation is often referred
to as aliasing.) This information can be important, e.g., to
allow the compiler to include, omit, or re-order operations to perform
optimisations.
There is a similar consideration in Fortran, where the mechanism can be copy-in, copy-out. There are some moderately complex rules on what is and what is not allowed to ensure that dummy arguments are independent, both from each other and from entities available via host association, or any other mechanism.
Broadly: any operation that affects the value of an argument must be taken via the associated dummy argument alone. This includes allocation status, and association status for pointers.
-
Consider a case where we have a subroutine of the form
subroutine my_array_update(ia, ib) integer, intent(inout) :: ia(:) integer, intent(inout) :: ib(:) ia(:) = ia(:) + 1 ib(:) = ib(:) / 2 end subroutine my_array_updateIf we were to make a
call my_array_update(a(1:10), a(6:15))we have a situation where both dummy arguments are referring to the same section of the single actual argument.Procedures which have only one
intent(inout)argument can reduce the scope for this potential problem. -
Consider a case where we have a module procedure, schematically:
module my_module ! ... integer, allocatable, public :: ihost(:) ! ... contains subroutine my_subroutine(iarg) integer, allocatable, intent(inout) :: iarg(:) ! ... change to the status of iarg(:) or ihost(:) ... end subroutine my_subroutine end module my_moduleWe now have a situation where ihost(:) may appear as the actual argument to
my_subroutine(). This is best avoided by avoiding module scope data.
These restrictions are not enforced by the compiler (it may not even be possible): violations by the programmer may just be manifest as undefined behaviour.
Actual and dummy arguments with target attribute
Likewise, there is a set of conditions on the use of
target attribute in the context of procedure arguments.
These may be summarised:
- Pointers associated with an actual argument may not become associated with relevant dummy arguments (copy-in, copy-out may occur);
- If a dummy argument has the
targetattribute, any pointer associated with the dummy argument may not be associated on return.
Procedure declarations
There is a procedure statement which declares a name to
be a procedure. In its simplest form, it is equivalent to an external
declaration:
procedure () :: f_external
external :: f_external
Here, the () indicates there is no interface information
available. For functions, one may include information on a return
type
procedure (integer) :: f_external
integer, external :: f_external
These are again equivalent.
The general form is
procedure [(interface-spec)] [, attribute-list ::] declaration-list
The parentheses accommodate an interface specification, which may be
an interface name, or a declaration type specification (such as
integer above). There are a number of possible attributes,
including pointer, which declares a pointer to a
procedure.
Pointers to functions or subroutines: procedure pointers
A procedure having an explicit interface may be the target of a procedure pointer. The declaration might be as follows:
interface
function my_external_function(x) result(y)
real, intent(in) :: x
real :: y
end function my_external_function
end interface
procedure (my_external_function), pointer :: f => my_external_function
! ...
y = f(x)
Note the name appearing in the interface needs to match that of the external function. This is sometimes referred to as a specific interface.
We may also define an abstract procedure, which may only appear in
the interface-name specification of a procedure
declaration.
abstract interface
function if_function(x) result(y)
real, intent(in) :: x
real :: y
end function if_function
end interface
An associated procedure definition might be
procedure (if_function) :: my_external_function
This is an alternative to the specific interface declaration above.
A procedure pointer must be associated in order to reference the procedure.
Exercise (10 minutes)
Procedure interfaces and pointers
An example of an external function is provided in
external.f90. This is a function which has a single
argument which is an integer rank 1 array, and returns an integer which
is the size of the array.
The accompanying program example2.f90 makes a simple
procedure declaration to allow the external function to be
referenced (similar to an external declaration).
$ ftn external.f90 example2.f90
There are a number of possible problems with this example (e.g., what happens if you provide an actual argument which is a rank two array?).
Adjust the example to provide a specific interface block which describes the external function. Make an appropriate procedure declaration, and also try declaring a pointer to the procedure.
Check this works and that the compiler now traps errors associated with incompatible actual arguments.
program example2
implicit none
interface
function array_size(a) result(isize)
real, dimension(:), intent(in) :: a
integer :: isize
end function array_size
end interface
procedure (array_size), pointer :: f => array_size
real :: a(13)
print *, "size of a is: ", f(a)
end program example2
Procedure interfaces and pointers (continued)
Try replacing the specific interface block with an equivalent abstract interface. Again, call the external function via a name declared in a procedure statement, and also try a procedure pointer.
program example2
implicit none
abstract interface
function my_array_size(a) result(isize)
real, dimension(:), intent(in) :: a
integer :: isize
end function my_array_size
end interface
procedure (my_array_size) :: array_size
real :: a(13)
print *, "size of a is: ", array_size(a)
end program example2
- Fortran pointers describe what they are pointing to, not only its address.
- We must take care when programming with pointers to avoid aliasing.
- Procedure pointers create a binding between a variable and a procedure.