12. Pointers
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.
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.
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.
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.
(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 |