Notes: Fortran 6.a

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

[Return to Math 60 Homepage | Return to Notes Listing Page]

Contents


Fortran-90/95/2003 Pointers [EF 17.1]

The concept of pointers in Fortran-90/95/2003 is similar, but not identical, to the concept of pointers in other languages that implement it. In brief, a pointer in Fortran-90/95/2003 is an alias, that is an alternative name, for the variable it points to. In practice, this means

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!

Declaring Simple Pointer Variables [EF 17.2]

To declare a pointer variable, one add the keyword POINTER in the variable declaration before the double colon, as follows:
REAL, POINTER :: Y

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:

REAL, TARGET :: X

Assignment of Pointer Variables [EF 17.3]

Fortran-90 introduced a pointer assignment operator which is indicated by using two symbols together in this way: =>

If X and Y are declared as in the previous section, and if one invokes the pointer assignment statement,

Y => X
then Y would, in effect, become an alias for X. One way to think about this, is that after the pointer assignment, in the symbol table, 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.

Using Pointer Variables for Linked Structures [EF 17.4]

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

Sample Program for Linked List [EF 17.5]

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

Cautions for Passing Pointer Variables to Subprograms [EF 17.6]

There are two major design features of Fortran which affect the use of pointer variables as parameters: Taken together, these two 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:

Sample Code: using CONTAINS

This code in this section and the next 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 "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.

Sample Code: using an INTERFACE Block [EF 17.8]

As mentioned in the previous section, in this section is a simple "translation" of the code found in Math 10 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. 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.
OUTPUT
                                      15
                             13
                                      14
                    9
                                      12
                             10
                                      11
           1
                                      8
                             6
                                      7
                    2
                                      5
                             3
                                      4

Pointers and Matrices [EF 17.9]

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.

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.

Caution [EF 17.10]

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


12

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.