Chapter 17
Essentials of Essentials of Fortran-90/95/2003
Pointers

Math 60 -- D. C. Smolarski, S.J.
Santa Clara University, Department of Mathematics and Computer Science

[Return to Math 60 Homepage | Return to Brief Contents Page]
[Return to Full Contents Page]

Contents


17.1 Fortran-90/95/2003 Pointers

The concept of pointers in Fortran-90/95/2003 is similar, but not identical, to the concept of pointers in other languages that implement pointers. In other languages, the values stored in memory locations associated with pointer identifiers commonly refer to an address of another location in memory rather than being interpreted as an integer, real, character, or other type of value. Via this (forwarding) address, pointer variables in other languages "point" to another section of memory and are useful in so-called linked structures (i.e., linked lists, trees, or graphs), in which each data "node" usually does not have its own identifier that refers to it.

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!

17.2 Declaring Simple Pointer Variables

To declare a pointer variable, one adds the attribute keyword POINTER in the variable declaration before the double colon, as follows:
         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.

17.3 Assignment of Pointer Variables

Fortran-90/95/2003 has introduced a pointer assignment symbol which is indicated by using two symbols (the equals and the greater than sign) together in this way: =>

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.

17.4 Using Pointer Variables for Linked Structures

One can use Fortran-90/95/2003 pointer variables to create nodes for linked structures in a way similar to what is done in other languages. One needs, however, to remember the distinctive character of Fortran-90/95/2003 pointers to avoid confusion with what is done in C/C++ or Pascal, for example.

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

17.5 Sample Program for Linked List

The following Fortran-90/95/2003 program creates three nodes and then prints out the contents of the INFO section of the nodes.
   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.)

17.6 Cautions for Passing Pointer Variables to Subprograms

There are three major design features of Fortran-90/95/2003 which affect the use of pointer variables as parameters: Taken together, these features demand special caution when using subprograms in which some parameters are pointer variables.

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:

17.7 Sample Code: using CONTAINS

This code in this section and the next generate a binary tree of a maximum of 16 nodes (in fact, only 15 nodes), labels the nodes according to the order of generation, and then prints out the tree (rotated sideways).

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.)

17.8 Sample Code: using an INTERFACE Block

As mentioned in the previous section, in this section is a simple "translation" of the code found in C++ program "mp10a.cxx" found in Notes N20 for Math 10.

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.

17.9 Pointers and Matrices

Pointers may also be used in conjuction with scalar and array variables. For example. one can declare two arrays as follows:
       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.

17.10 Caution

One needs to remember carefully what Fortran-90/95/2003 pointers really do in order to interpret code properly. Pointers are aliases for other, previously-defined variables or they are names for newly allocated memory space. A pointer assignment means that the pointer variable is associated with an existing variable and becomes an alias for the memory location formerly associated with the existing variable. (But the contents of the memory location has not been changed!) After a pointer assignment, one may use the standard arithmetic assignment to change the value of whatever is stored in that memory location, i.e., the "contents."

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.