For a general introduction to pointers (in C++) see this link.
One major difference in the ways "pointers" can be used in Fortran-90/95/2003 is that they can also be used to "point" to a section of a large data structure, for example, to a row or column of an array. In addition, they can be used as variables in their own right. Thus, the concept of a "pointer" variable in Fortran-90/95/2003 is much more versatile than similar variables in other programming languages.
Note that pointer variables were not implemented in older versions of FORTRAN!
One also needs to identify those variables to which Y can point, and this is done by adding the keyword TARGET in the variable declarations of such variables, as follows:
If X and Y are declared as in the previous section, and if one invokes the pointer assignment statement,
As an example, a list node can be defined as a new "derived" data type using a pointer variable as follows:
TYPE NODE
INTEGER :: INFO
TYPE (NODE), POINTER :: NEXT
END TYPE NODE
After defining this new data type NODE, one can then
declare variables to be pointers to elements of this data type
in the usual way, for example:
TYPE (NODE), POINTER :: LIST, TEMP
This statement would declare LIST and TEMP
to be variables which could be used as pointers to
memory sections of derived type NODE.
After declaration, LIST and TEMP do not contain any information, nor do they "point" to any node. To allocate memory for a node, one makes use of the library statement ALLOCATE, which is similar in function to the Pascal procedure new or the C++ function new. Invoking the following statement
ALLOCATE(LIST)
causes memory to be allocated for a derived variable of
the type of which LIST is declared to be (i.e., in this
case, of type NODE) and then the identifier LIST
can be used to access the fields of the node.
To store information in the INFO section of LIST, one uses the standard Fortran-90 % operator for a derived variable type, e.g.,
LIST%INFO = 1
A pointer variable can be set or reset to "null" by invoking the
library statement NULLIFY. (This has the same effect
as using the Pascal nil or the C++ NULL in
an assignment statement.) In Fortran-90, this statement is
used as in this example:
NULLIFY(LIST%NEXT)
To test whether a pointer variable is associated with some
memory location (i.e., another variable) or whether it has been
"nullified," one uses the LOGICAL library function
ASSOCIATED. For example, the following loop would
print out the values found in the INFO sections of
nodes of a linked list until the NEXT section of some
node were found to be null:
DO
WRITE(6,*) LIST%INFO
IF ( .NOT. ASSOCIATED(LIST%NEXT)) EXIT
LIST => LIST%NEXT
END DO
PROGRAM SAMPLE
IMPLICIT NONE
! define new data type "NODE"
TYPE NODE
INTEGER :: INFO
TYPE (NODE), POINTER :: NEXT
END TYPE NODE
! declare variables of type "NODE"
TYPE (NODE), POINTER :: LIST, TEMP
! create first node -- allocate space
! and assign values to each section of the node.
ALLOCATE(LIST)
LIST%INFO = 1
NULLIFY(LIST%NEXT)
! let TEMP point to this node
TEMP => LIST
! create second node -- LIST points to
! new node while TEMP points to old node
ALLOCATE(LIST)
LIST%INFO = 2
LIST%NEXT => TEMP
! since new node is now linked to old
! node, TEMP can be reset to point to
! new node
TEMP => LIST
! create third node
ALLOCATE(LIST)
LIST%INFO=3
LIST%NEXT=>TEMP
! write out contents of nodes
! This starts with the node LIST points to,
! which is the last node created.
! LIST is repeatedly reset to the NEXT node,
! ending with the first node created.
DO
WRITE(6,*) LIST%INFO
IF( .NOT. ASSOCIATED(LIST%NEXT)) EXIT
LIST => LIST%NEXT
END DO
END PROGRAM SAMPLE
(This code is available for downloading at this link.)
Because of the ability to compile subprograms separately, when such subprograms include pointer variables as parameters, the calling segment (main program or another subprogram) needs to be given special information about these parameters. This information can be given in one of three ways:
The code in this section makes use of "internal" definition of subprograms, by means of the CONTAINS statement. In this approach, the NODE data type defined in the main program is "visible" to each subprogram and need not be repeated or conveyed through a MODULE.
PROGRAM TREESAMPLE
IMPLICIT NONE
TYPE NODE
TYPE (NODE), POINTER :: LEFT
INTEGER :: INFO
TYPE (NODE), POINTER :: RIGHT
END TYPE NODE
TYPE (NODE), POINTER :: ROOT
INTEGER COUNT,NUMNODE
! (* initialization *)
NUMNODE = 0
COUNT = 0
ROOT => GENTREE(16,NUMNODE)
CALL TREEPRINT(ROOT, COUNT)
! end of main program -- inclusion of
! "internal" subprogram code follows:
! NOTE: the data type NODE receives its
! definition from the main program
CONTAINS
!* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!* F U N C T I O N G E N T R E E *
! This generates a tree. n is a small positive integer
! specifying an upper bound on the number of nodes
! of the tree. The nodes can have two children, and
! an information field. The information field is
!* given a sequential node number. *
!
!* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
RECURSIVE FUNCTION GENTREE(N, NUMNODE) RESULT (T)
IMPLICIT NONE
INTEGER N, NUMNODE
TYPE (NODE), POINTER ::T
ALLOCATE(T)
NUMNODE=NUMNODE+1 !numnode is the unique number
!stored in each node after its creation
T%INFO = NUMNODE
IF (N<=2) THEN
NULLIFY(T%LEFT)
ELSE
T%LEFT => GENTREE(N/2,NUMNODE)
END IF
IF (n<=2) THEN
NULLIFY(T%RIGHT)
ELSE
T%RIGHT => GENTREE(N/2,NUMNODE)
END IF
END FUNCTION
!* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!* S U B R O U T I N E T R E E P R I N T *
!* The information fields of the nodes of a tree
! are printed so that the tree is rotated 90 degrees
! counter-clockwise. The nodes are given proper
!* depths. *
!* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
RECURSIVE SUBROUTINE TREEPRINT(T, COUNT)
IMPLICIT NONE
TYPE (NODE), POINTER :: T
INTEGER COUNT, I
COUNT = COUNT+1
!count serves as a counter of the depth
!of the node and permits the correct
!spacing before the node contents are printed.
IF (ASSOCIATED(T)) THEN
CALL TREEPRINT(T%RIGHT,COUNT)
WRITE(6,*)(" ", i=0,COUNT-1), T%INFO
CALL TREEPRINT(T%LEFT,COUNT)
END IF
COUNT=COUNT-1
END SUBROUTINE
END PROGRAM
This code is also accessible at this link
for downloading.
The code in this section makes use of "external" definition of subprograms, but conveys the necessary information to pass pointer variables into SUBROUTINE TREEPRINT through an INTERFACE block. In this approach, the NODE data type which is needed in in the main program and each subprogram needed to be conveyed to each segment through a MODULE (or it could be repeated in place in each segment).
MODULE TREENODE
TYPE NODE
TYPE (NODE), POINTER :: LEFT
INTEGER :: INFO
TYPE (NODE), POINTER :: RIGHT
END TYPE NODE
END MODULE
PROGRAM TREESAMPLE
USE TREENODE
IMPLICIT NONE
TYPE (NODE), POINTER :: ROOT
INTEGER COUNT,NUMNODE
! we include the interface for TREEPRINT here
INTERFACE
RECURSIVE SUBROUTINE TREEPRINT(T, COUNT)
USE TREENODE
TYPE (NODE), POINTER :: T
INTEGER COUNT
END SUBROUTINE
RECURSIVE FUNCTION GENTREE(N, NUMNODE) RESULT (T)
USE TREENODE
TYPE (NODE), POINTER :: T
INTEGER N, NUMNODE
END FUNCTION
END INTERFACE
! (* initialization *)
NUMNODE = 0
COUNT = 0
ROOT => GENTREE(16,NUMNODE)
CALL TREEPRINT(ROOT, COUNT)
END PROGRAM
!* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!* F U N C T I O N G E N T R E E *
! This generates a tree. n is a small positive integer
! specifying an upper bound on the number of nodes
! of the tree. The nodes can have two children, and
! an information field. The information field is
!* given a sequential node number. *
!* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
RECURSIVE FUNCTION GENTREE(N, NUMNODE) RESULT (T)
USE TREENODE
IMPLICIT NONE
INTEGER N, NUMNODE
TYPE (NODE), POINTER ::T
ALLOCATE(T)
NUMNODE=NUMNODE+1 !numnode is the unique number
!stored in each node after its creation
T%INFO = NUMNODE
IF (N<=2) THEN
NULLIFY(T%LEFT)
ELSE
T%LEFT => GENTREE(N/2,NUMNODE)
END IF
IF (n<=2) THEN
NULLIFY(T%RIGHT)
ELSE
T%RIGHT => GENTREE(N/2,NUMNODE)
END IF
END
!* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!* S U B R O U T I N E T R E E P R I N T *
!* The information fields of the nodes of a tree
! are printed so that the tree is rotated 90 degrees
! counter-clockwise. The nodes are given proper
!* depths. *
!* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
RECURSIVE SUBROUTINE TREEPRINT(T, COUNT)
USE TREENODE
IMPLICIT NONE
TYPE (NODE), POINTER :: T
INTEGER COUNT, I
COUNT = COUNT+1
!count serves as a counter of the depth
!of the node and permits the correct
!spacing before the node contents are printed.
IF (ASSOCIATED(T)) THEN
CALL TREEPRINT(T%RIGHT,COUNT)
WRITE(6,*)(" ", i=0,COUNT-1), T%INFO
CALL TREEPRINT(T%LEFT,COUNT)
END IF
COUNT=COUNT-1
END
This code is also accessible at this link
for downloading.
15
13
14
9
12
10
11
1
8
6
7
2
5
3
4
REAL, TARGET :: B(10,10)
REAL, POINTER :: A(:,:)
This means that A has been declared as a matrix but has
no memory locations of its own. The compiler knows that it will
have two dimensions, which will be conveyed to A when it
is associated with another identifier in the program.
What can we do with matrix A? Let's look at four things.
(1) The matrix A could be linked to B via a pointer assignment as follows:
A => B
(2) It could also be given new memory space (as with an ALLOCATABLE
array) using the ALLOCATE operation:
ALLOCATE (A(5,5))
To relinquish the space to which A points, one can use
DEALLOCATE (A)
(3) One can also use the ASSOCIATED function (see 17.4 above) with
two parameters to see whether a pointer variable is associated with
a specific target, for example:
ASSOCIATED (A,B)
which will return a value of .TRUE. or .FALSE.
(4) One may also associate a pointer variable with only part of a target object. For example, given B as declared above, one could set A to a subsection of B such as:
A => B(1:5,1:5)
Suppose that we had C and D declared as one
dimension pointer arrays as follows:
REAL, POINTER :: C(:), D(:)
Then we could associate C and D with sections
of B as follows:
C => B(6,:)
D => B(:,7)
Thus C is another name for the row 6 of matrix B, and
D is another name for column 7 of matrix B.
Given the following code segment,
REAL, TARGET :: A
REAL, POINTER :: P, Q
A = 2.71828
P => A
Q => P
A = 765.4321
WRITE(*,*) Q
A has been set to 2.71828 and P is associated with
the same memory location as A. Then Q becomes associated
with P and thereby also with A. So, Q, P,
and A all refer to one and the same memory location. Thus,
when A is changed to 765.4321, the values of Q
and P are both also changed! Thus, what gets printed out is
765.4321.
If the code continued with the following statements
Q = 3.14159
WRITE(*,*) A
what would get printed out is 3.14159. Note the use of the arithmetic
assignment with the pointer variable Q which means "change
the contents of the memory location associated with Q to 3.14159."
Since Q is really a different name for A, by
changing Q, the value of A also is reset.
This page is maintained by Dennis C. Smolarski, S.J.
dsmolarski@math.scu.edu
© Copyright 1998, 1999, 2006 Dennis C. Smolarski, SJ, All rights reserved.
Last changed: 24 January 2006.