12. Pointers

Introduction.

Pointers have been included in Fortran 90, but not in the usual way as in most other languages, with pointer as a specific data type. Here they are rather understood as an attribute to the other data types. The reason for this new way to introduce them is that by having a special data type for pointers, the risk of erroneous use of the pointer is very large. A variable with a pointer attribute can be used as a usual variable and in some new ways. Pointers in Fortran 90 are thus not memory addresses as in other programming languages (and in certain Fortran implementations) but rather an extra name (alias).

The increased security is obtained not only through that each variable, which shall be used as a pointer, must be given an attribute POINTER, but also that all variables, that will be pointed to, must be given an attribute TARGET. An example explaining how to do this follows.

       REAL, TARGET          :: B(10,10)
       REAL, POINTER         :: A(:,:)
       A => B
The matrix B has been specified completely, i.e. with the dimensions given explicitly. In addition, it has been stated that it can be the target of a pointer. The matrix A, which can be used as a pointer, has to be declared as a matrix, i.e. to be given a correct number of dimensions, a correct rank, but the extent for this is decided later, at the assignment (and in reality the assignment is a pointer-association) which is done with the symbols =>. Please note that the pointer assignment does not mean that the data in the matrix B is copied over to the matrix A (which would have taken relatively large resources), but it is merely a new address that is generated. To "move" data with the pointer concept will therefore be very efficient. As an alternative the pointer can become associated with the statement ALLOCATE, and be disassociated with DEALLOCATE, as in the following example.
       ALLOCATE (A(5,5))
       DEALLOCATE (A)
There is also an internal function ASSOCIATED in order to investigate if a pointer is associated (and if it is associated with a certain target) and a statement NULLIFY in order to terminate the association.
IF ( ASSOCIATED (A) )  WRITE(*,*) ' A is associated '
IF ( ASSOCIATED (A, B) )  WRITE(*,*) ' A is associated with B'
NULLIFY  (A)
Please remember that a pointer in Fortran 90 has both type and rank, and that these must agree with the corresponding target. This increases the security at the use of pointers, it is therefore not possible by mistake to let a pointer change values of variables of other (different) data types. The fact that you have to specify that a variable can be a target also increases both security and efficiency of the compilation.

Important application of pointers are lists and trees, and especially dynamic arrays.

Simple use of pointers.

You have to be careful when you use pointers. In the following simple example we look at ordinary scalar floating-point numbers.
       REAL, TARGET   :: A
       REAL, POINTER  :: P, Q
       A = 3.1416
       P => A
       Q => P
       A = 2.718
       WRITE(*,*) Q
Here the value of Q equals 2.718 since both P and Q point towards the same variable A and that one has just changed its value from 3.1416 to 2.718. We now make a simple variation.
       REAL, TARGET  :: A, B
       REAL, POINTER :: P, Q
       A = 3.1416     
       B = 2.718
       P => A  
       Q => B
Now both the values of A and P are equal to 3.1416 and the values of both B and Q are 2.718. If we now give the statement
       Q = P
all four variables will get the value 3.1416, which means that an ordinary assignment of pointer variables has the same effect as the conventional assignment
       B = A
If we instead give a pointer association
       Q => P
then the three variables A, P and Q all have the value 3.1416, while B contains the value 2.718. In the second case Q only points to the same variable as P while in the first case Q becomes the same as P, and the value addressed by Q becomes equal to the value addressed by P.

Pointers and arrays.

A simple use of pointers is to give a name to an array section.
       REAL, TARGET   :: B(10,10)
       REAL, POINTER  :: A(:), C(:)
        A => B(4,:)   ! vector A becomes the fourth row
        C => B(:,4)   ! and vector C becomes the fourth
                      ! column of the matrix B
It is not necessary to take the whole section, you can take only a partial section. In the following example you can take a partial matrix WINDOW of a large matrix MATRIX.
       REAL, TARGET          :: MATRIX(100,100)
       REAL, POINTER         :: WINDOW(:,:)
       INTEGER               :: N1, N2, M1, M2
       WINDOW => MATRIX(N1:M1, N2:M2)
If you later wish to change a dimension of the partial matrix WINDOW you only need to make a new pointer association. Please note that the indices in WINDOW are not from N1 to M1 and from N2 to M2 but from 1 to M1-N1+1 and from 1 to M2-N2+1.

There does not exist arrays of pointers directly in Fortran 90, but you can construct such facilities by creating a new data type. An example is to store a lower (or left) triangular matrix with rows with varying length. First introduce a new data type ROW

       TYPE ROW
              REAL, POINTER   :: R(:)
       END TYPE
and then specify the two lower triangular matrices V and L as vectors of rows with varying length
       INTEGER                :: N
       TYPE(ROW)              :: V(N), L(N)
after which you can allocate the matrix V as below (and in the corresponding way you can allocate the matrix L)
       DO I = 1, N
              ALLOCATE (V(I)%R(1:I))    
              ! Various length of rows
       END DO
The statement
       V = L
then becomes equivalent with
       V(I)%R => L(I)%R
for all the components, i.e. all values of I. Please note that in this application there is no TARGET required.

Allocation of arrays using pointers.

One implementation of dynamic memory allocation is to use pointers to specify an array. In the following example we specify a vector in such a way, that it can be given its size (its extent) in a subroutine, but can be used in the main program. It is the only way we have found to move an actual dimension upwards.

An alternative method has however been suggested by Arie ten Cate, using a module with an ALLOCATEd and SAVEd array. An example is available.

We however use an INTERFACE with pointers in the main program and allocate, also using pointers, a vector in the subroutine. In this way we get a dynamically allocated vector.

      PROGRAM MAIN_PROGRAM
      INTERFACE
         SUBROUTINE SUB(B)
         REAL, DIMENSION (:), POINTER :: B
         END SUBROUTINE SUB
      END INTERFACE
      REAL, DIMENSION (:), POINTER :: A
      CALL SUB(A) 
!     Now we can use the vector A.
!     Its dimension was determined in the subroutine,
!     the number of elements is available as SIZE(A).
      END PROGRAM MAIN_PROGRAM

      SUBROUTINE SUB(B)
      REAL, DIMENSION (:), POINTER :: B
      INTEGER M
!     Now we can assign a value to M, for example
!     through an input statement.
!     When M has been assigned we can allocate B 
!     as a vector.
      ALLOCATE (B(M))
!     Now we can use the vector B.
      END SUBROUTINE SUB
Note: The method above is even more useful for allocating matrices, see exercise 12.3.

Exercises

(12.1) Use pointers in order to assign all even elements of a vector the value 13 and all odd elements of a vector the value 17.
Solution.

(12.2) Specify two pointers, and let one of them point to a whole vector and the other one point to the seventh element of the same vector.
Solution.

(12.3) Use pointers to specify a matrix in such a way, that it is given its size (its extent) in a subroutine but can be used in the main program.
Solution.


Last modified: 7 September 1998
boein@nsc.liu.se