In contrast, a pointer in Fortran-90/95/2003 is an alias, that is, an alternative name, for the variable to which it points. In practice, this means
For a general introduction to pointers (in C++) see this link.
One major difference in the ways "pointers" may be used in Fortran-90/95/2003 is that they can "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, "pointer" variables in Fortran-90/95/2003 may be used in much more versatile ways than similar variables in other programming languages.
Note that pointer variables were not implemented in earlier versions of Fortran!
REAL, POINTER :: Y
One normally also needs to identify those variables to which Y (or any other appropriate pointer) can point. This is done by adding the attribute keyword TARGET in the variable declarations of such variables, as follows:
REAL, TARGET :: X
Adding such attributes provide safeguards not present in other
languages with pointers.
If X and Y are declared as in the previous section, and the code executes the pointer assignment statement,
Y => X
then Y "points" to X and would, in effect,
become an alias for
X. One way to understand what occurs is as follows: after the
pointer assignment, in the symbol table (i.e., the table
which stores all the identifiers used in the program segment
and the corresponding memory locations), the memory
location associated with X is now also the
memory location associated with Y. Thus if the value of
X changes, so does the value of Y and vice versa,
since both X and Y refer to one and the same memory
location.
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
(Note that this is a recursive definition, in that one component
of the new datatype NODE also has the datatype 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 declares that LIST and TEMP
are identifiers which could be used as pointers to
memory sections of derived type NODE.
Note, however, that after the declaration, LIST and TEMP do not contain any information, nor do they "point" to any node. This is so because there have been no variables declared with a "target" pointer attribute and no assocation has taken place!
To allocate memory for a node, one may make use of the library statement ALLOCATE, which is similar in function to the C++ function new, the C function malloc, or the Pascal procedure new when used with pointer variables. Invoking the following statement
ALLOCATE(LIST)
causes memory to be allocated for a variable of
the type of which LIST has been declared to be (i.e., in this
case, of the derived type NODE). The identifier LIST
can then be used to access the fields of the node.
To store information in the INFO section of LIST, one uses the standard Fortran-90/95/2003 % 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/95/2003, this statement is
used as in this example:
NULLIFY(LIST%NEXT)
Fortran-95 introduced an alternative function to set a pointer null, called NULL(). This function may be used without any argument in most cases. (It does need an argument when it is difficult to determine from context what is the form of the pointer that is being set to "null.") This function may be used in Fortran-95/2003 to initialize a pointer in a derived type definition. The previous example is equivalent to:
LIST%NEXT => NULL()
To test whether a pointer variable is associated with some memory location (i.e., another variable) or whether it has been "nullified," one can use the LOGICAL library function ASSOCIATED. As an example, the following loop prints out the values found in the INFO sections of nodes of a linked list until the NEXT section of some node is 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 a 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 the 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 the second node -- LIST points to
! new node while TEMP points to old node
ALLOCATE(LIST)
LIST%INFO = 2
LIST%NEXT => TEMP
! since the new node is now linked to old
! node, TEMP can be reset to point to
! new node
TEMP => LIST
! create the third node
ALLOCATE(LIST)
LIST%INFO=3
LIST%NEXT=>TEMP
! write out the 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:
They are simple "translations" of the code found in C++ program "mp10a.cxx" found in Notes N20 for Math 10.
The code in this section makes use of "internal" definition of subprograms, by means of the CONTAINS statement (cf. Section 10.3.1). 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. We also use an INTERFACE to convey the necessary information about FUNCTION GENTREE since it returns a pointer type. In this approach, the NODE data type which is needed in in the main program and each subprogram is 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 and GENTREE 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.
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.
The matrix A could be linked to B via a pointer assignment as follows:
A => B
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)
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.
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-2005,
Dennis C. Smolarski, S.J., All rights reserved.
Last changed: 28 June 2005.