XLF: Integer-Pointer ("CRI-Pointer", "Cray-Pointer")

Integer-Pointer unterstützen dynamisches Aliasing und Adressarithmetik.

Integer-Pointer sind eine nichtstandardisierte Spracherweiterung, die überhaupt nichts mit dem standardisierten Pointer-Konzept zu tun hat. Mit ihrer Hilfe kann man nichtportable Programme erzeugen, die zudem noch schwer lesbar sind. Genau zu diesem Zweck werden sie gerne in kommerzieller Anwender-Software eingesetzt.


Syntax

POINTER (pointer1, pointee1) [, (pointer2, pointee2)]...

Mit der Integer-POINTER-Anweisung spezifiziert man zusammengehörende Variablenpaare, nämlich jeweils eine Pointer-Variable pointer und eine pointer-basierte Variable pointee.

pointer ist eine skalare Variable vom Typ INTEGER(4) (in 32-Bit-Umgebungen) oder INTEGER(8) (in 64-Bit-Umgebungen). Ihr Typ darf nicht explizit vereinbart werden.
pointee ist eine skalare Variable oder Feld-Variable (mit oder ohne nachfolgende Dimensionierung) beliebigen (auch benutzerdefinierten) Typs.

Zum Zugriff auf den Speicher benutzt man nur die pointer-basierte Variable pointee. Bei jedem Bezug auf die pointer-basierte Variable pointee wird tatsächlich die (Basis-)Adresse verwendet, die zuvor in der zugehörigen Pointer-Variablen pointer gespeichert worden ist. Ein pointer-basiertes Feld kann nicht nur in der Integer-POINTER-Anweisung dimensioniert werden, sondern auch in einer Typvereinbarungs-Anweisung oder in einer DIMENSION-Anweisung.

Verwendung

Es sind vier Schritte in dieser Reihenfolge erforderlich:
  1. Spezifikation der pointer-basierten Variablen pointee und der zugehörigen Pointer-Variablen pointer in einer Integer-POINTER-Anweisung.
  2. Spezifikation des Datentyps und (im Fall eines pointer-basierten Feldes) der Gestalt der pointer-basierten Variablen pointee. Der Typ der zugehörigen Pointer-Variablen pointer wird nicht vereinbart.
  3. Definition der Pointer-Variablen pointer mit dem Wert der Basis-Adresse eines Speicherbereiches ausreichender Größe.
  4. Verwendung der pointer-basierten Variablen pointee in ausführbaren Fortran-Anweisungen. Die aktuelle Adresse dieser Variablen pointee wird von der zugehörigen Pointer-Variablen pointer geholt.
Achtung:

Speicherzuordnung

Der Compiler belegt selbst keinen Speicher für die pointer-basierte Variable pointee, vielmehr hat der Programmierer dafür zu sorgen, dass während der Laufzeit Speicher zugeordnet wird. Das geschieht in der Weise, dass die zugehörige Pointer-Variable pointer mit dem Wert einer Adresse definiert wird. Das kann auf folgende Art und Weise geschehen:

Speicherzuordnung mittels LOC-Funktion

Die Zuordnung existierenden Speichers an eine pointer-basierte Variable erfolgt üblicherweise mittels der LOC-Funktion; beispielsweise
      COMMON pool
      INTEGER, DIMENSION (1000) :: pool
      INTEGER :: zwei_d
      POINTER (ip, zwei_d(10,10))
      ip = LOC (pool(1))
      :

Auf eine pointer-basierte Variable (hier zwei_d) darf nur zugegriffen werden, nachdem ihre zugehörige Pointer-Variable mit einer Adresse definiert worden ist. Die Dimensionierung eines pointer-basierten Feldes kann in der POINTER-, in einer Typvereinbarungs- oder in einer DIMENSION-Anweisung erfolgen.

Die LOC-Funktion darf für alle Argumente aufgerufen werden, die eine zusammenhängende Speicherfolge belegen oder einer zusammenhängenden Speicherfolge zugeordnet sind. Dazu gehören unter dieser Voraussetzung auch Datenstrukturen, Formalparameter-Felder beliebiger Art, dynamische Felder (mit ALLOCATABLE-Attribut, Status "allokiert"), automatische Felder und Pointer (Status "zugeordnet"). Auch auf solche Objekte kann man also mit einer pointer-basierten Variablen zugreifen, wenn man zuvor die zugehörige Pointer-Variable mit der Startadresse dieser Objekte füttert.

Verschiedene Zuordnungen über Integer-POINTER

Zuordnung über Integer-Pointer ist eine spezielle Form der Zuordnung über den Speicher ("storage association").

     POINTER (pa,a)
     pa = LOC(b)              ! a und b werden einander zugeordnet

     POINTER (pa,a), (pa,b)   ! a und b sind einander zugeordnet

     POINTER (pa,a), (pb,b)
     pa = LOC(c)
     pb = pa                  ! a, b und c werden einander zugeordnet

     POINTER (pa,a)
     :
     CALL sub (pa,b)
     :
     SUBROUTINE (p,x)
     POINTER (p,y)
     p = LOC(x)               ! a und b des rufenden (Unter-)Programmes 
                              ! werden einander zugeordnet

Speicherzuordnung per dynamische Allokation

Zum Allokieren/Deallokieren von Speicher pointer-basierter Felder können nicht die standardisierten Anweisungen ALLOCATE/DEALLOCATE verwendet werden, sondern die System-Routinen malloc und free. Dabei handelt es tatsächlich um C-Funktionen, was man bei der Angabe der Argumente berücksichtigen muss.

Die System-Routinen malloc und free sollte man aus der Bibliothek libhm.a nehmen. Sie sind schneller als die "normalen" AIX-Versionen der Routinen.

xlf95 -qalias=intptr -qddim test.f90 -lhm

    PROGRAM main
    :
    POINTER (ptr, feld(nvar,nrec))
    REAL(8) feld
    :
    nvar = 20
    nrec = 30
    ptr = malloc(%val(nvar*nrec*8))  ! weil C-Funktion, Uebergabe "by value"
    ! es werden 20*30*8 Bytes Speicher allokiert und
    ! die Startadresse wird dem Pointer ptr zugewiesen
    :
    feld(10,15) = ...
    :
    CALL free(%val(ptr))             ! Uebergabe "by value"
    :
    END

Mit der free-Funktion darf nur solcher Speicher an das System zurückgegeben werden, der zuvor mit der malloc-Funktion allokiert worden ist und der noch zugänglich ist (d.h. noch nicht mittels free freigegeben worden ist). Die Compiler-Option -qalias=intptr sollte man bei Verwendung von Integer-Pointer immer angeben. Die Compiler-Option -qddim hat was mit variablen pointer-basierten Feldern zu tun (s. weiter unten).

Adressarithmetik

Eine Pointer-Variable darf in jedem Ausdruck und in jeder Anweisung verwendet werden, wo auch andere INTEGER(4)- bzw. INTEGER(8)-Variable verwendet werden dürfen. Wenn man eine Pointer-Variable (re)definiert, muss man daran denken, dass hier in jedem Fall mit Byte-Adressen operiert wird.
      COMMON pool
      INTEGER, DIMENSION (1000) :: pool
      INTEGER :: zwei_d
      POINTER (ip, zwei_d(10,10))
      ip = LOC(pool(1))
      :
      ip = ip + 100*4  ! alles in Bytes
      :
Nebenbei bemerkt: Man kann sogar mit Absolutadressen arbeiten, indem man die Pointer-Variable direkt mit einer Absolutadresse definiert; beispielsweise
      ip = 804397376

Pointer-basierte Felder

Einem pointer-basierten Feld wird über die zugehörige Pointer-Variable ein Speicherbereich zugeordnet. Die Pointer-Variable kann aber nur eine Adresse halten. Deshalb muss der zugeordnete Speicherbereich zusammenhängend sein. Und das pointer-basierte Feld muss in der POINTER-, Typvereinbarungs- oder DIMENSION-Anweisung wie ein Feld mit Speicherfolge (also wie ein Feld mit expliziter Gestalt oder mit übernommener Größe) spezifiziert werden.

Variable pointer-basierte Felder

Ein pointer-basiertes Feld kann auch als variables Feld spezifiziert werden. Das ist dann der Fall, wenn man in den Spezifikationsausdrücken zur Dimensionierung der einzelnen Indexbereiche Variablen angibt, beispielsweise lokale Variablen, Formalparameter, COMMON-Variablen, USE-zugeordnete oder umgebungszugeordnete Variablen.

Wenn man die Compiler-Option -qddim angibt, dann darf man variable pointer-basierte Felder nicht nur in Unterprogrammen, sondern auch im Hauptprogramm spezifizieren, und die Indexgrenzen werden bei jedem Zugriff auf das pointer-basierte Feld neu berechnet. Zum Beispiel:

         INTEGER pointee, n, feld(10)
         POINTER (pointer, pointee(n))
         :
         n = 5
         pointer = LOC(feld(2))
         PRINT *, pointee   ! druckt feld(2) bis feld(6)
         :
         n = 7              ! pointee-Feld vergroessern
         PRINT *, pointee   ! druckt feld(2) bis feld(8)

Wenn man die Compiler-Option -qddim nicht angibt, dann wird die variable Dimension in einem Unterprogramm einmalig bei Eintritt der Kontrolle in das Unterprogramm berechnet (d.h. nicht erst beim Zugriff auf das pointer-basierte Feld).

Einschränkungen

Es gibt eine Reihe weiterer Einschränkungen:
  1. Ein pointer-basiertes Feld darf nicht mit der Feldlänge Null spezifiziert werden.
  2. Eine Pointer-Variable darf nicht mit der Adresse eines Feldes der Länge Null definiert werden, außer ihm ist über den Speicher eine nichtleere Speicherfolge zugeordnet.
  3. Eine pointer-basierte Variable darf nur eine skalare Variable sein oder eine Feldvariable, deren Gestalt wie ein Feld mit übernommener Größe oder mit expliziter Gestalt spezifiziert ist.
  4. Eine pointer-basierte Variable darf nicht in COMMON-, DATA-, NAMELIST- oder EQUIVALENCE-Anweisungen auftreten.
  5. Eine pointer-basierte Variable darf folgende Attribute nicht haben:
    EXTERNAL, ALLOCATABLE, POINTER, TARGET, INTRINSIC, INTENT, OPTIONAL, SAVE, STATIC, AUTOMATIC oder PARAMETER.
  6. Eine pointer-basierte Variable darf kein Formalparameter sein und darf daher nicht in FUNCTION-, SUBROUTINE- oder ENTRY-Anweisungen auftreten.
  7. Eine pointer-basierte Variable darf kein Funktionswert sein.
  8. Eine pointer-basierte Variable darf keine automatische Variable (eines Unterprogrammes) sein.
  9. Der Name einer pointer-basierten Variablen darf nicht mit dem Namen eines generischen Schnittstellenblockes übereinstimmen.
  10. Der Datentyp einer pointer-basierten Variablen benutzerdefinierten Typs muss die SEQUENCE-Eigenschaft haben.
  11. Eine pointer-basierte Variable darf keine Pointer-Variable sein. D.h. Integer-POINTER können nicht aufeinander zeigen.
  12. Eine Pointer-Variable darf folgende Attribute nicht haben:
    DIMENSION, POINTER, TARGET, PARAMETER, ALLOCATABLE, EXTERNAL oder INTRINSIC.
  13. Der Name einer Pointer-Variablen darf nicht als NAMELIST-Gruppenname auftreten.
  14. Der Name einer Pointer-Variablen darf kein Unterprogrammname sein.
  15. Die LOC-Funktion darf nicht für einen (Standard-)Zeiger aufgerufen werden, der den Status undefiniert oder nichtzugeordnet hat.
  16. Eine pointer-basierte Variable darf nur dann einem Teilfeld zugeordnet werden, wenn dessen Speicherfolge zusammenhängend ist.

Portabilität???

Programme, die Integer-Pointer enthalten, sind hochgradig nichtportabel,


11. Dez 2002        Wilhelm Gehrke         gehrke@rrzn.uni-hannover.de