Fortran: Fortran 95: Zeiger
<<< zur Fortran-Startseite | |
<< Fortran 95 | Fortran 2003 >> |
< Ein- und Ausgabe | Vektoren- und Matrizenrechnung > |
Was sind Zeiger?
BearbeitenEin Zeiger (Pointer) ist eine Variable, die Adresse und (Daten-)Typ enthält – ähnlich wie bei einem Array, das rank und shape enthält. Der Zeiger (Pointer) charakterisiert Position, Länge und Organisation des Speicherabschnitts, an dem die Variable (Target) gehalten wird.
Zeiger in Fortran 95
BearbeitenIn Fortran 95 werden Zeiger durch Zufügen des Attributes pointer
bei der Deklaration von Variablen erzeugt.
datentyp, pointer :: variable |
Ein so deklarierter Zeiger kann auf andere Zeiger oder auf mittels target
gekennzeichnete Variablen verweisen.
datentyp, target :: variable |
Die Zeigerzuordnung erfolgt durch das Symbol
=> |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real, pointer :: ptr real, target :: trg trg = 5.5 ptr => trg write(*,*) ptr ! Ausgabe: 5.50000 ! Zeiger werden bei Bedarf automatisch dereferenziert end program bsp |
Assoziationsstatus
BearbeitenEin Zeiger kann einen der folgenden Assoziationzustände annehmen:
- undefiniert (dangling)
- nicht zugeordnet (disassociated, null)
- zugeordnet (associated)
Der Zuordnungsstatus eines Zeigers ist unmittelbar nach der Deklaration undefiniert. Mittels zeiger => null()
oder nullify(zeiger)
kann ein Zeiger auf einen nicht zugeordneten Status gesetzt werden. Verweist ein Zeiger auf einen anderen zugeordneten Zeiger oder ein Target, so ist sein Zustand zugeordnet.
Der Assoziationsstatus eines Zeigers lässt sich über die Funktion
associated (zeiger [, ziel])
abfragen. Sinnvoll ist eine derartige Abfrage nur dann, wenn der Zuordnungsstatus nicht undefiniert ist.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, pointer :: ptr1 => null() character(20), pointer :: ptr2 character(20), target :: str str = "Hallo, Welt!" ptr2 => str write(*,*) associated(ptr1) ! Ausgabe: F write(*,*) associated(ptr2) ! Ausgabe: T write(*,*) associated(ptr2, ptr1) ! Ausgabe: F write(*,*) associated(ptr2, str) ! Ausgabe: T end program bsp |
Speicherplatz dynamisch reservieren und freigeben
BearbeitenFür normale Variablen läuft die Speicherplatzverwaltung automatisch ab. Bisher wurden Zeiger immer solchen normalen (Target)Variablen, für die bereits Speicherplatz reserviert war, zugeordnet. Aber auch für Zeiger selbst kann Speicherplatz reserviert werden. Bei der Zeigerdeklaration ist der Zeigerstatus undefiniert oder nicht zugeordnet. Eine Wertzuweisung an eine solche Zeigervariable würde zur Laufzeit einen Speicherzugriffsfehler ergeben. Die Funktion
allocate (zeiger1, [zeiger2, ...] [,stat=integervar])
reserviert in Abhängigkeit des Zeiger-Datentyps Speicherplatz für die einzelnen Zeiger. Die Funktion
deallocate (zeiger1, [zeiger2, ...] [,stat=integervar])
gibt diesen Speicherplatz wieder frei.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, pointer :: ptr1 => null(), ptr2 => null() integer :: status allocate(ptr1, stat = status) ptr1 = 2222 write (*,*) "Status = ", status ! Wenn status = 0, dann wurde erfolgreich Speicherplatz reserviert write (*,*) ptr1 ! Ausgabe: 2222 ptr2 => ptr1 ptr1 = 5555 write (*,*) ptr1, ptr2 ! Ausgabe: 5555 5555 deallocate(ptr1) end program bsp |
Zeiger und Felder
BearbeitenBeispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(:), pointer :: ptr => null() integer, dimension(5:10), target :: arr = (/55, 66, 77, 88, 99, 111/) ptr => arr write(*,*) ptr ! Ausgabe: 55 66 77 88 99 111 ptr => arr(7:) write(*,*) ptr ! Ausgabe: 77 88 99 111 end program bsp |
Beispiel: "ragged array"
Fortran 90/95-Code (free source form) |
program bsp implicit none type element integer, dimension(:), pointer :: ptr end type element integer, dimension(5), target :: a = (/ 1, 2, 3, 4, 5 /) integer, dimension(2), target :: b = (/ 99, 55 /) integer, dimension(4), target :: c = (/ -11, -12, -13, -14 /) integer :: i type( element ), dimension(3) :: rarr rarr(1)%ptr => a rarr(2)%ptr => b rarr(3)%ptr => c do i = 1,3 write (*,*) "rarr(", i, "): ", rarr(i)%ptr end do ! Ausgabe ! rarr( 1 ): 1 2 3 4 5 ! rarr( 2 ): 99 55 ! rarr( 3 ): -11 -12 -13 -14 end program bsp |
Verkettete Listen
BearbeitenIn der Regel ist die Größe einer Liste vor ihrer Erstellung (zum Bsp. durch Einlesen einer Datei) nicht bekannt, was die Verwendung von Array erschwert. Dies lässt sich durch Pointer vereinfachen. Man unterscheidet dabei viele verschiedene Listentypen:
- linear und einfach verkettet: Diese Liste lässt sich nur in eine Richtung durchlaufen und das Ende hat keinen Bezug zum Anfang.
- zyklisch und einfachverkettet: Nur eine Durchlaufrichtung, aber das Ende ist mit dem Anfang verpointert. Nach dem vollständigen Durchlaufen beginnt man wieder am Anfang.
- linear und doppelt verkettet: Lineare Liste, in der jedes Element auf das nächste UND das vorherige zeigt. Der Durchlauf der Liste ist so in zwei Richtungen möglich. Der Anfang ist dabei nicht mit den Ende verpointert.
- zyklisch und doppelt verkettet: Zyklische Liste innerhalb derer zwei Durchlaufrichtungen zur Verfügung stehen.
Die Wahl des Listentyps richtet sich nach dem Verwendungszweck. Einfach verkettete Listen sind auch einfach in der Implementierung. Für den Zugriff entgegen der Durchlaufrichtung muss die Liste jedoch einmal vollständig durchlaufen werden, was den Rechenaufwand und vor allem die Rechenzeit erhöht. Sie eignen sich daher gut für einfache Aufgaben mit gerichteter Abarbeitung. Doppelt verkettete Listen lassen mehr Freiraum hinsichtlich ihr Durchlaufrichtung. Sie sind jedoch komplexer in Ihrer Implementierung und erhöhen den Speicheraufwand.
Anfügen von Listenelementen
BearbeitenBeispiel: Lineare, einfach verkettete Liste (LIFO)
Fortran 90/95-Code (free source form) |
module m1 implicit none type node integer :: id character(5) :: value type(node), pointer :: next => null() end type type(node), pointer :: first => null() private ! Auf alle nachfolgenden Anweisungen kann von aussen nicht zugegriffen werden public :: add_node, write_all, free_all ! Auf die Subroutinen add_node, write_all und free_all ! kann von aussen explizit zugegriffen werden, ! jedoch nicht auf innerhalb der Subroutinen ! deklarierte Datentypen contains subroutine add_node(id, str) implicit none integer, intent(in) :: id character(5), intent(in) :: str type(node), pointer :: new, tmp ! Speicher reservieren allocate(new) ! Werte setzen new%id = id new%value = str ! Am Beginn der Liste einfügen if (.not. associated(first)) then first => new else tmp => first first => new first%next => tmp end if end subroutine add_node subroutine write_all() implicit none type(node), pointer :: tmp tmp => first do if (associated(tmp) .eqv. .FALSE.) exit write(*,*)tmp%id, tmp%value tmp => tmp%next end do end subroutine write_all subroutine free_all() implicit none type(node), pointer :: tmp do tmp => first if (associated(tmp) .eqv. .FALSE.) exit first => first%next deallocate(tmp) end do end subroutine free_all end module m1 program bsp use m1 implicit none call add_node (1, "AAAAA") call add_node (2, "BBBBB") ! ... call add_node (150, "ZZZZZ") call write_all ! Ausgabe: ! 150 ZZZZZ ! 2 BBBBB ! 1 AAAAA call free_all ! Die verkettete Liste wird wieder freigegeben end program bsp |
Beispiel: Zyklische einfach verkettete Liste:
Fortran 90/95-Code (free source form) |
module m1 implicit none private public: ... type node integer :: id character(5) :: value type(node), pointer :: next => null() end type type(node), pointer :: last => null() !externer Zeiger auf die zyklische Liste ... ! Vergleich oben, subroutine add_node(id, str) subroutine add_node(id, str) implicit none integer, intent(in) :: id character(5), intent(in) :: str type(node), pointer :: new, tmp ! Speicher reservieren allocate(new) ! Werte setzen new%id = id new%value = str ! Listen Elemente werden in Reihenfolge des Einlesen verpointert if (.not. associated(last)) then last => new new%next => new ! das erste Element muss auf sich selbst zeigen für den zkylus else tmp => last last => new new%next => tmp%next tmp%next => new end if end subroutine ... |
Löschen von Listenelementen
BearbeitenFür das Löschen eines Elementes aus einer Liste lässt sich eine kurze Subroutine schreiben, welche die Zeiger neu setzt und das Element löscht. Man kann dabei in einfach verketteten Listen nicht ohne größeren Aufwand das Element löschen, welches soeben in der Liste betrachtet wird, sondern nur das nachfolgende Element.
Fortran 90/95-Code (free source form) |
SUBROUTINE del_next(pntr) TYPE(liste),POINTER :: pntr !Subroutine bekommt einen externen Pointer übergeben TYPE(liste),POINTER :: tmp => NULL() tmp => pntr ! pntr wird in tmp zwischengespeichert pntr => pntr%next ! pntr wird um zwei Positionen weitergesetzt pntr => pntr%next DEALLOCATE(tmp%next) ! tmp%next wird gelöscht tmp%next => pntr ! Verbindung wird neu gesetzt END SUBROUTINE |
<<< zur Fortran-Startseite | |
<< Fortran 95 | Fortran 2003 >> |
< Ein- und Ausgabe | Vektoren- und Matrizenrechnung > |