Pointers are a new feature to the Fortran standard and bring Fortran 90 into line with languages like C. The use of pointers can provide:
type, POINTER [,attr] :: variable list
type, TARGET [,attr] :: variable list
Where:
For example, the following three pairs of statements, all declare pointers and one or more variables which may be targets:
REAL, POINTER :: pt1
REAL, TARGET :: a, b, c, d, e
INTEGER, TARGET :: a(3), b(6), c(9)
INTEGER, DIMENSION(:), POINTER :: pt2
INTEGER, POINTER :: pt3(:,:)
INTEGER, TARGET :: b(:,:)
Note that the following is an examples of an illegal pointer declaration:
REAL, POINTER, DIMENSION(10) :: pt !illegal
The POINTER attribute is incompatible with the ALLOCATABLE, EXTERNAL, INTENT, INTRINSIC, PARAMETER and TARGET attributes. The TARGET attribute is incompatible with the EXTERNAL, INTRINSIC, PARAMETER and POINTER attributes.
pointer => target
Where pointer is a pointer variable and target is any valid target. pointer may now be used as an alias to the data stored by target. The pointer assignment operator also allocates storage required by the pointer.
To change the value of a pointer's target (just like changing the value of a variable) use the usual assignment operator (=). This is just as it would be for other variable assignment with a pointer used as an alias to another variable.
The following are examples of pointer assignment:
INTEGER, POINTER :: pt
INTEGER, TARGET :: x=34, y=0
...
pt => x ! pt points to x
y = pt ! y equals x
pt => y ! pt points to y
pt = 17 ! y equals 17
The declaration statements specify a three variables, pt is an integer pointer, while x and y are possible pointer targets. The first executable statement associates a target with pt. The second executable statement changes the value of y to be the same as pt's target, this would only be allowed when pt has an associated target. The third executable statement re-assigns the pointer to another target. Finally, the fourth executable statement assigns a new value, 17, to pt's target (not pt itself!). The effect of the above statements is illustrated below.
It is possible to assign a target to a pointer by using another pointer. For example:
REAL, POINTER :: pt1, pt2
...
pt2 => pt1 !legal only if pt1 has an associated target
Although this may appear to be a pointer pointing to another pointer, pt2 does not point to pt1 itself but to pt1's target. It is wrong to think of `chains of pointers', one pointing to another. Instead all pointers become associated with the same target.
Beware, of using the following statements, they are both illegal:
pt1 => 17 !constant expression is not valid target
pt2 => pt1 + 3 !arithmetic expression is not valid target
Pointer are automatically dereferenced when they appear:
pt => a
b = pt !b equals a, pt is dereferenced
IF( pt<0 ) pt=0 !pt dereferenced twice
WRITE(6,*) pt !pt's target is written
READ(5,*) pt !value stored by pt's target
NULLIFY( list of pointers )
A pointer that has been nullified may be thought of as pointing `at nothing'.
The status of a pointer may be found using the intrinsic function:
ASSOCIATED ( list of pointers [,TARGET] )
The value returned by ASSOCIATED is either .TRUE. or .FALSE. When TARGET is absent, ASSOCIATED returns a value .TRUE. if the pointer is associated with a target and .FALSE. if the pointer has been nullified. When TARGET is present ASSOCIATED reports on whether the pointer points to the target in question. ASSOCIATED returns a value .TRUE. if the pointer is associated with TARGET and .FALSE. if the pointer points to another target or has been nullified.
It is an error to test the status of an undefined pointer, therefore it is good practice to nullify all pointers that are not immediately associated with a target after declaration.
The following example shows the use of the ASSOCIATED function and the NULLIFY statement:
REAL, POINTER :: pt1, pt2 !undefined status
REAL, TARGET :; t1, t2
LOGICAL :: test
pt1 => t1 !pt1 associated
pt2 => t2 !pt2 associated
test = ASSOCIATED( pt1 ) ! .T.
test = ASSOCIATED( pt2 ) ! .T.
...
NULLIFY( pt1 ) !pt1 disassociated
test = ASSOCIATED( pt1 ) ! .F.
test = ASSOCIATED( pt1, pt2 ) ! .F.
test = ASSOCIATED( pt2, TARGET=t2) ! .T.
test = ASSOCIATED( pt2, TARGET=t1) ! .F.
NULLIFY( pt1, pt2) !disassociated
The initial undefined status of the pointers is changed to associated by pointer assignment, there-after the ASSOCIATED function returns a value of .TRUE. for both pointers. Pointer pt1 is then nullified and its status tested again, note that more than one pointer status may be tested at once. The association status of pt2 with respect to a target is also tested. Finally both pointers are nullified in the same (last) statement.
REAL, POINTER :: p, pa(:)
INTEGER :: n=100
...
ALLOCATE( p, pa(n) )
...
DEALLOCATE( p, pa )
In the above example p points to an area of dynamic memory and can hold a single, real number and pa points to a block of dynamic memory large enough to store 100 real numbers. When the memory is no longer required it may be deallocated using the DEALLOCATE statement. In this respect pointers behave very much like allocatable arrays.
INTEGER, POINTER :: pt(:)
...
ALLOCATE( pt(25) )
NULLIFY( pt ) !wrong
Since the pointer is the only way to reference the allocated storage (i.e. the allocated storage has no associated variable name other than the pointer) reassigning the pointer means the allocated storage can no longer be released. Therefore all allocated storage should be deallocated before modifying the pointer to it.
REAL, POINTER :: p1, p2
...
ALLOCATE( p1 )
p2 => p1
DEALLOCATE( p1 ) !wrong
In the above example p2 points to the storage allocated to p1, however when that storage is deallocated p2 no longer has a valid target and its state becomes undefined. In this case dereferencing p2 would produce unpredictable results.
Programming errors like the above can be avoided by making sure that all pointers to a defunked target are nullified.
REAL, TARGET :: grid(10,10)
REAL, POINTER :: centre(:,:), row(:)
...
centre => grid(4:7,4:7)
row => grid(9,:)
An array pointer can be associated with the whole array or just a section. The size and extent of an array pointer may change as required, just as with allocatable arrays. For example:
centre => grid(5:5,5:6) !inner 4 elements of old centre
Note, an array pointer need not be deallocated before its extent or bounds are redefined.
INTEGER, TARGET :: list(-5:5)
INTEGER, POINTER :: pt(:)
INTEGER, DIMENSION(3) :: v = (/-1,4,-2/)
...
pt => list !note bounds of pt
pt => list(:) !note bounds of pt
pt => list(1:5:2)
pt => list( v ) !illegal
The extent (or bounds) of an array section are determined by the type of assignment used to assign the pointer. When an array pointer is aliased with an array the array pointer takes its extent form the target array; as with pt => list above, both have bounds -5:5. If the array pointer is aliased to an array section (even if the section covers the whole array) its lower bound in each dimension is 1; as with pt => list(:) above, pt's extent is 1:11 while list's extent is -5:5. So pt(1) is aliased to list(-5), pt(2) to list(-4), etc.
It is possible to associate an array pointer with an array section defined by a subscript triplet. It is not possible to associate one with an array section defined with a vector subscript, v above. The pointer assignment pt => list(1:5:2) is legal with pt(1) aliased to list(1), pt(2) aliased to list(3) and pt(3) aliased to list(5).
The dynamic nature of pointer arrays can provide varying amounts of storage for a derived data type:
TYPE data
REAL, POINTER :: a(:)
END TYPE data
TYPE( data ) :: event(3)
DO i=1,3
READ(5,*) n !n varies in loop
ALLOCATE( event(i)%a(n) )
READ(5,*) event(i)%a
END DO
The number of values differs for each event, the size of the array pointer depends on the input value n. When the data is no longer required the pointer arrays should be deallocated:
DO i=1,3
DEALLOCATE( event(i)%a )
END DO
TYPE node
REAL :: item
TYPE( node ), POINTER :: next
END TYPE node
The derived type node contains a single object item (the data in the list) and a pointer next to another instance of node. Note the recursion-like property in the declaration allowing the pointer to reference its own data type.
Linked lists are a very powerful programming concept, their dynamic nature means that they may grow or shrink as required. Care must be taken to ensure pointers are set up and maintained correctly, the last pointer in the list is usually nullified. Details of how to implement, use and manipulate a linked list can be found in some of the reading material associated with these notes.
When the actual argument is a pointer and the corresponding dummy argument is not, the pointer is dereferenced and it is the target that is copied to the dummy argument. On return the target takes the value of the dummy argument. This requires the actual argument to be associated with a target when the procedure is referenced.
For example:
PROGRAM prog
INTERFACE !needed for external subroutine
SUBROTINE suba( a )
REAL, POINTER :: a(:)
END SUBROUTINE suba
END INTERFACE
REAL, POINTER :: pt(:)
REAL, TARGET :: data(100)
...
pt => data
CALL suba( pt )
CALL subb( pt )
...
CONTAINS
SUBROUTINE subb( b ) !internal
REAL, DIMENSION(:) :: b !assumed shape of 100
...
END SUBROUTINE subb
END PROGRAM prog
SUBROUTINE suba( a ) !external subroutine
REAL, POINTER :: a(:) !a points to data
...
END SUBROUTINE suba
It is not possible for a non-pointer actual argument to correspond with a pointer dummy argument.
INTERFACE
FUNCTION max_row ( a )
REAl, TARGET :: a(:,:)
REAL, POINTER :: max_row(:)
END FUNCTION max_row
END INTERFACE
REAL, TARGET :: a(3,3)
REAL, POINTER :: p(:)
...
p => max_row ( a )
...
FUNCTION max_row ( a ) !external
REAL, TARGET :: a(:,:)
REAL, POINTER :: max_row(:) !function result
INTEGER :: location(2)
location = MAXLOC( a ) !row and column of max value
max_row => a(location(1),:) !pointer to max row
END FUNCTION max_row
Here the external function max_row returns the row of a matrix containing the largest value. The pointer result is only allowed to point to the dummy argument a because it is declared as a target, (otherwise it would have been a local array and left the pointer dangling on return). Notice the function result is used on the right hand side of a pointer assignment statement. A pointer result may be used as part of an expression in which case it must be associated with a target.
(a) A pointer to a single element of an array of 20 integers.
(b) A pointer to a character string of length 10.
(c) An array pointer to a row of a 10 by 20 element real array.
(d) A derived data type holding a real number three pointers to neighbouring nodes, left, right and up (this kind of derived data structure may be used to represent a binary tree).
(a) The first row of the target.
(b) A loop which associates the pointer with each column of the target in turn.
REAL, POINTER :: pt(:)
REAL, TARGET, DIMENSION(-10:10, -10:10) :: a
Read in the number and values for a set of experimental readings, say 4, and output their mean. Deallocate all pointers before the program finishes.