2-16 DYNAMIC MEMORY ALLOCATION AND POINTERS
 *******************************************
 (Thanks to Sergio Gelato who contributed most of this chapter)

 In standard FORTRAN 77, the sizes of all objects must be known at
 compile time (This does not apply to the sizes of formal arguments 
 to subprograms, only to those of the actual arguments).

 This is inconvenient for many applications, as it means that the 
 program may have to be recompiled before it can be run with a 
 different problem size. Many operating environments let the user 
 allocate contiguous blocks of memory whose sizes are determined 
 at run time. Many implementations of FORTRAN 77 support an extension 
 ("Cray pointers") that allows programs to make use of this facility. 

 Cray pointers also allow the construction of linked data structures 
 such as lists, trees, queues, ... More recently, the Fortran 90 
 standard introduced roughly equivalent functionality in the form 
 of ALLOCATABLE arrays on the one hand, and of the POINTER attribute 
 on the other.


 Declaring Cray pointers 
 -----------------------
 Cray pointers are just variables (usually of type INTEGER, but it 
 is best not to declare their type explicitly) that hold the address 
 of another variable called the pointee.

 Example of pointer declaration:

      INTEGER    N
      PARAMETER  (N = 10)
      REAL       POINTEE(N)
      POINTER    (PTR, POINTEE)

 Note that we have here a type statement of only ONE entity: a pointer 
 called PTR, whose data type is 'pointer to a 1D array of REAL with
 dimension N'. This multi-line type statement is a little awkward, but 
 obeys FORTRAN syntax.

 The array that PTR points to can be accessed by the POINTEE identifier, 
 but POINTEE is not an array by itself as it has (until PTR is initialized) 
 no memory storage associated with it.

 The above example is confusing because of a common misunderstanding 
 about FORTRAN type-statements (e.g. REAL X,Y), such statements ARE NOT
 like the declarations of PASCAL and C, they don't reserve memory 
 storage, but just supply data-type information to the compiler.

 Put another way, with Cray pointers the pointer and the entity it 
 points to are declared together and have different identifiers 
 associated with them, there is no separate indirection (dereferencing) 
 operator.


 An example program with Cray pointers:


      PROGRAM PNTR
C     ------------------------------------------------------------------
      INTEGER	
     *			I
C     ------------------------------------------------------------------
      REAL		
     *			ARRAY1(10),   ARRAY2(5), 
     *			POINTEE1(10), POINTEE2(5), POINTEE3(*)
C     ------------------------------------------------------------------
      POINTER 
     *			(PTR1, POINTEE1),
     *			(PTR2, POINTEE2),
     *			(PTR3, POINTEE3)
C     ------------------------------------------------------------------
      DATA 
     *			ARRAY1   /0,1,2,3,4,5,6,7,8,9/,
     *			ARRAY2   /5,5,5,5,5/
C     ------------------------------------------------------------------
      WRITE(*,*) 
      WRITE(*,'(1X,A,10F6.1)') 'array1=   ', ARRAY1
      WRITE(*,'(1X,A,10F6.1)') 'array2=   ', ARRAY2
C     ------------------------------------------------------------------
      WRITE(*,*) 
      PTR1 = LOC(ARRAY1)
      PTR2 = LOC(ARRAY1)
      PTR3 = LOC(ARRAY1)
      WRITE(*,'(1X,A,10F6.1)') 'pointee1= ', POINTEE1
      WRITE(*,'(1X,A,10F6.1)') 'pointee2= ', POINTEE2
      WRITE(*,'(1X,A,10F6.1)') 'pointee3= ', (POINTEE3(I), I = 1, 10)
C     ------------------------------------------------------------------
      WRITE(*,*) 
      PTR1 = LOC(ARRAY2)
      PTR2 = LOC(ARRAY2)
      PTR3 = LOC(ARRAY2)
      WRITE(*,'(1X,A,10F6.1)') 'pointee1= ', POINTEE1
      WRITE(*,'(1X,A,10F6.1)') 'pointee2= ', POINTEE2
      WRITE(*,'(1X,A,10F6.1)') 'pointee3= ', (POINTEE3(I), I = 1, 5)
C     ------------------------------------------------------------------
      END


 The result of this program on a VMS machine was:

 array1=      0.0   1.0   2.0   3.0   4.0   5.0   6.0   7.0   8.0   9.0
 array2=      5.0   5.0   5.0   5.0   5.0

 pointee1=    0.0   1.0   2.0   3.0   4.0   5.0   6.0   7.0   8.0   9.0
 pointee2=    0.0   1.0   2.0   3.0   4.0
 pointee3=    0.0   1.0   2.0   3.0   4.0   5.0   6.0   7.0   8.0   9.0

 pointee1=    5.0   5.0   5.0   5.0   5.0   0.0   0.0   0.0   0.0   0.0
 pointee2=    5.0   5.0   5.0   5.0   5.0
 pointee3=    5.0   5.0   5.0   5.0   5.0


 Note that declaring the pointee with assumed-array syntax, makes it
 impossible tohave I/O statements that reference the array name 
 without subscript. 



 Using Cray pointers
 -------------------
 The value of the pointer can be defined using some intrinsic 
 function usually called LOC(arg) that takes as argument the 
 name of a variable and returns it's memory address.

 The following points should be noted:

    1) The pointee only exists while the value of the pointer 
       is defined.
    2) The pointee may not be part of a COMMON block, nor may 
       it be a dummy argument of the current subprogram. 
       The pointer may be a COMMON block variable or a dummy 
       argument.
    3) You may pass a pointee as an actual argument to a 
       subprogram without special precautions; i.e., the 
       subprogram need not know that the object is a pointee; 
       the subprogram will treat it as it would any ordinary 
       variable.
    4) If you pass a pointer as an actual argument, then the 
       called subprogram should usually have declared the 
       corresponding dummy argument as a pointer.

 Furthermore:

    -- If the pointee is an array, and the dimensions of the array 
       are specified as run-time expressions rather than compile-time 
       constants, these will usually be evaluated (in an arbitrary 
       order, so beware of side-effects in their evaluation) upon 
       entry into the subprogram.

       It follows that you can't declare such variable dimensions 
       in the main program. A few compilers offer, as an option, 
       "dynamic dimensioning" in which the array dimensions are 
       evaluated again on each reference. (For example, XL Fortran 
       lets you do this by compiling with -qddim.)

       Dynamic dimensioning is not as widely supported as Cray pointers, 
       and can entail additional overhead in some circumstances 
       (especially when working with multidimensional arrays). You are 
       therefore encouraged to arrange for the dimensions to be known 
       upon entry to the subprogram that declares the pointee; otherwise 
       multidimensional arrays may be indexed incorrectly, and bounds 
       checking may fail on one-dimensional arrays.

    -- Many implementations supply a subprogram to allocate memory 
       (it may be called MALLOC, for Memory ALLOCation, or HPALLOC, 
       for HeaP ALLOCation, or something else entirely) and another 
       subprogram to release memory to the operating system. 

       On VMS you may use: STATUS = LIB$GET_VM(SIZE_IN_BYTES, PTR)
       to allocate a block of memory, to free that block you may use:
       STATUS = LIB$FREE_VM(SIZE_IN_BYTES, PTR). STATUS is an integer 
       variable used to store the return value of these system routines,
       (STATUS .NE. .TRUE.) means the routine failed.

       It is your responsibility to allocate a pointee before the 
       value of the pointer becomes undefined (for example by exiting
       the subprogram where it is declared, if it isn't SAVEd.)


 Cray pointers and automatic compiler optimizations
 --------------------------------------------------
 Compilers perform a partial data-flow analysis as a preparation before 
 automatic optimization, unrestricted pointers that are free to point 
 to any other variable makes such an analysis almost impossible. 

 Cray pointers are restricted to some degree, e.g. pointer arithmetic 
 is not allowed, but still a pointer can point to different objects
 during a procedure activation. 

 Fortran was designed so it could be highly optimized (remember that
 it had to compete with assembly language and won), the language 
 specification explicitly forbids anything that may lead to ALIASING 
 (having more than one name for the same variable) because of the 
 detrimental effect on automatic optimizations. In short, pointers 
 violate the spirit of Fortran, and defeat its purpose.

 Cray pointers may even cause WRONG results when compiler optimization 
 is turned on, and they are used without deep understanding of the effect 
 on the optimizer. A probable reason for that may be that the optimizer 
 assumes no-aliasing.

 It is recommended that you use Cray pointers only for allocating
 dynamic memory, and pass the corresponding pointees to subroutines. 

 If you wish to avoid Cray pointers completely, you can call a C routine 
 for that purpose, see the chapter on variable-size arrays for source-code 
 and discussion.


 Fortran 90 ALLOCATABLE objects
 ------------------------------
 Fortran 90 offers a more elegant way of allocating objects dynamically,
 through the ALLOCATABLE attribute and the ALLOCATE and DEALLOCATE
 executable statements. 

 For example, one can declare a one-dimensional allocatable array 
 as:

   REAL, ALLOCATABLE :: A(:)

 then allocate it to a given size with: 

   INTEGER IAS
   ALLOCATE (A(3:12), STAT=IAS)

 The optional STAT=IAS allows recovery from allocation errors. If the
 allocation is unsuccessful and no STAT= was given, the program aborts;
 if STAT= was given, IAS contains either 0 for success, or a non-zero 
 error code.

 The array bounds are stored with the array, and are computed at the
 time the ALLOCATE statement is executed. This avoids all the difficulties
 mentioned above with the dimensioning of Cray pointees.

 Don't forget to 

   DEALLOCATE(A)

 when you no longer need it. If you are no longer sure whether A is 
 allocated or not, say:

   IF(ALLOCATED(A)) DEALLOCATE(A) .



 Linked data structures and the Fortran 90 POINTER attribute
 -----------------------------------------------------------
 Pointers are also useful for constructing linked lists, trees and other
 dynamic data structures. Cray pointers are suitable for this purpose,
 particularly when used in conjunction with the function LOC(arg) that
 returns a pointer to its argument, but Fortran 90's ALLOCATABLE arrays
 are not. This is why Fortran 90 also supports a POINTER attribute in
 addition to ALLOCATABLE. A binary tree node could be declared as

   TYPE NODE
      TYPE(NODE), POINTER :: LEFT, RIGHT
      TYPE(ELEMENT) :: DATA
   END TYPE NODE

 Fortran 90 pointers can be in one of three states: undefined, associated
 and unassociated. The initial state is unassociated. An unassociated
 pointer may become associated by executing a pointer assignment statement

   pointer => target

 or an allocation statement

   ALLOCATE(pointer);

 an associated pointer may become unassociated by executing a

   NULLIFY(pointer)

 or, in some cases,

   DEALLOCATE(pointer);

 any pointer may become undefined in the same way as other Fortran 
 variables can (by exiting a subprogram without a SAVE for the pointer, 
 etc.) and once undefined may never be used again. (Be careful!)

 Pointers may point to otherwise unnamed objects created by an ALLOCATE
 statement, or to named objects declared with the TARGET attribute.
 The TARGET attribute is designed as an optimization aid to the compiler;
 if you use it on variables that don't need it, your code may run more 
 slowly.


   Summary of differences between Cray and Fortran 90 pointers
   ===========================================================

 Cray                                   Fortran 90
 -------------------------------------  ------------------------------------
 POINTER is a data type                 POINTER is an attribute

 A pointer holds the machine address    A pointer holds any information
 of the pointee; arithmetic is usually  needed to access the object; usually
 possible on pointer values, but may    an address plus a data type, array
 be non-portable.                       bounds and strides where applicable.
                                        You should not depend on assumptions
                                        about the internal representation,
                                        storage size, etc. of a Fortran 90
                                        pointer. No arithmetic on pointers.

 Assigning to a pointer variable        Referencing the pointer variable
 associates the pointer with a new      "does the right thing", i.e. usually
 pointee. Referencing the pointee       manipulates the value of the pointee.
 variable manipulates the current       In particular, pointer = value will
 pointee without affecting the          change the value of the object pointed
 pointer.                               to, but not the association of the
                                        pointer with the pointee. If you want
                                        to change that, use pointer => target
                                        instead.

 POINTER is used both to construct      POINTER can be used for both linked
 linked data structures and to support  data structures and dynamic allocation,
 dynamic memory allocation.             but if dynamic allocation alone is
                                        needed it is better to use ALLOCATABLE
                                        objects. (Significant performance
                                        differences have been reported with
                                        some compilers.)



Return to contents page