Kapitel 8: Datenfelder (Arrays)

Inhaltsverzeichnis

Kapitel 10: Bedingungen

9. Pointer (Zeiger)

Der Umgang mit Pointern ist nicht besonders anfängerfreundlich — bei falscher Verwendung können Sie einen Programmabsturz oder Schlimmeres verursachen. Es ist also äußerste Vorsicht geboten, und Sie sollten nur dann eigene Experimente mit Pointern anstellen, wenn Sie wissen, was Sie tun.

Dieses Kapitel ist sehr kurz und soll Ihnen nur einen schnellen Einblick in den Umgang mit Pointern geben, da in späteren Artikeln grundlegende Kenntnisse über Pointern hilfreich sind.

9.1 Speicheradresse ermitteln

Variablen sind, wie wir uns erinnern, Speicherplätze, in denen Werte „aufbewahrt“ werden können. Sie besitzen einen Variablennamen, über den sie angesprochen werden können, und einen Wert, der im Speicherplatz hinterlegt wurde. Nicht zuletzt hat die Variable aber auch einen Platz im Speicherbereich des Programmes, also eine Adresse, an der die Variable gefunden werden kann. Das Anlegen einer Variablen ist eigentlich nicht ganz so trivial, wie es bisher den Eindruck hatte — zunächst muss ein ausreichend großer freier Speicherbereich reserviert werden, da ja nicht etwa zwei verschiedene Variablen an dieselbe Stelle schreiben und damit den alten Wert der anderen Variable zerstören sollen. Wenn die Variable nicht mehr benötigt wird (spätestens am Ende des Programmes, oft aber schon wesentlich früher) ist wieder eine Freigabe des Speicherbereichs nötig, damit der Speicherbedarf nicht ständig anwächst. Wenn Sie DIM verwenden, kümmert sich zum Glück der Compiler um die korrekte Reservierung, Verwaltung und Freigabe des Speicherbereichs.

Dennoch — jede Variable besitzt eine Adresse, die den ihr zugeordneten Speicherbereich angibt. Diese Adresse lässt sich über einen Zeiger, oder auf englisch Pointer, ansprechen. Dieser Pointer wird ausgegeben, wenn vor dem Variablennamen ein @ gesetzt wird.

DIM AS INTEGER x
PRINT "x befindet sich an der Speicherstelle "; @x
SLEEP

Die Variable wird bei jedem Start des Programmes an einer anderen Stelle abgelegt. Bei der Adresse handelt es sich, je nach Betriebssystem, um einen 32-Bit- oder 64-Bit-Wert.

9.2 Pointer deklarieren

Es können auch Variablen direkt als Pointer deklariert werden, was bedeutet, dass sie eine Adresse speichern anstatt eines „normalen“ Wertes. Pointer-Variablen besitzen denselben Wertebereich und Speicherplatzbedarf wie eine UINTEGER-Variable (also 32-Bit in einem 32-Bit-System und 64-Bit in einem 64-Bit-System). Insofern könnten Pointer wie UINTEGER behandelt werden. FreeBASIC unterscheidet die beiden Datentypen jedoch intern voneinander und gibt eine Warnung aus, wenn sie im Programm nicht säuberlich voneinander getrennt werden, da ein falscher Pointerzugriff zu erheblichen Problemen führen kann.

Eine Pointer-Variable wird wie eine normale Variable deklariert, wobei auf den Variablentypen das Schlüsselwort POINTER oder häufiger (da kürzer) PTR folgt. POINTER und PTR sind in diesem Zusammenhang gleichbedeutend, und es macht keinen Unterschied, welches der beiden Schlüsselwörter Sie verwenden.

DIM AS SINGLE PTR x
DIM y AS INTEGER PTR

Um auf den Wert zuzugreifen, der an einer bestimmten Adresse hinterlegt ist, wird vor die Pointer-Variable ein Stern * gestellt. Nach der Deklaration besitzt die Variable standardmäßig den Wert 0 (in diesem Fall spricht man dann von einem Nullpointer). Ein lesender oder schreibender Zugriff auf diese Adresse würde den Laufzeitfehler Segmentation fault 1 auslösen. Man kann der Pointer-Variablen jedoch zuvor die Adresse einer anderen Variablen zuweisen.

Quelltext 9.1: Pointerzugriff
DIM AS INTEGER x = 5      ' normaler INTEGER-Wert
DIM AS INTEGER PTR p      ' Pointer auf einen INTEGER-Wert

' Zuweisung einer Adresse
p = @x
PRINT "x liegt bei der Adresse "; p;
PRINT " und besitzt den Wert"; *p

' Variable x ueber ihren Pointer veraendern
*p = 12
PRINT "x besitzt nun den Wert "; x
SLEEP

Pointer werden vor allem dann benötigt, wenn eigene Speicherbereiche verwaltet werden sollen, etwa ein Grafikspeicher. In diesem Fall muss der Speicher jedoch selbst reserviert und auch wieder freigegeben werden. Auch bei der Übergabe von Speicherbereichen an externe Bibliotheken werden gern Pointer verwendet. Das beliebteste Austauschformat für Zeichenketten ist der ZSTRING PTR, weil für das Speicherformat nur bekannt sein muss, dass die Zeichenkette mit einem Nullbyte endet.

9.3 Speicherverwaltung bei Arrays

Bei einem Array werden, wie in Kapitel 8 erwähnt, die Einträge im Speicher direkt hintereinander abgelegt.

Quelltext 9.2: Speicherverwaltung bei Arrays
DIM AS DOUBLE  d(2)         ' 3 DOUBLE-Eintraege
DIM AS SHORT   s(1, 1)      ' 2x2 SHORT-Werte

PRINT "Position der DOUBLE-Werte d():"
PRINT @d(0), @d(1), @d(2)
PRINT
PRINT "Position der SHORT-Werte s():"
PRINT @s(0, 0), @s(0, 1), @s(1, 0), @s(1, 1)
SLEEP
Ausgabe:
Position der DOUBLE-Werte d():
3214830252    3214830260    3214830268

Position der SHORT-Werte s():
3214830212    3214830214    3214830216    3214830218

Die Ausgabe ist natürlich nur ein mögliches Beispiel. Sie sehen jedoch mehreres:

  • Die DOUBLE-Werte folgen im Abstand von 8 Bit aufeinander, die SHORT-Werte im Abstand von 2 Bit. Das entspricht der Größe des jeweiligen Datentyps.

  • Bei mehrdimensionalen Arrays folgen im Speicher zuerst die Werte aufeinander, die denselben ersten Indexwert besitzen.

  • Die beiden Speicherbereiche der Arrays müssen nicht nebeneinander liegen, auch wenn die Arrays direkt nacheinander definiert wurden. Das Programm sucht sich jeweils eine passende „Lücke“ im Speicherblock, in die das Array am Stück untergebracht werden kann.

9.4 Fragen zum Kapitel

Das Verständnis von Pointern ist für Einsteiger sicher nicht so einfach, und es ist nicht schlimm, wenn im Moment noch ein paar Fragen offen bleiben. Ein paar davon habe ich hier zusammengestellt:

  1. Pointer — was ist das überhaupt?

  2. Wozu brauche ich sowas?

  3. Wie groß ist der Pointer einer SHORT-Variablen? Wie groß ist der Pointer auf einen STRING?


Fußnoten:
1) Unter Windows war ein Segmentation fault früher unter dem Namen „Allgemeine Schutzverletzung“ bekannt; bei aktuellen Versionen erscheint in diesem Fall eine Meldung wie „<Programmname> funktioniert nicht mehr“.


Kapitel 8: Datenfelder (Arrays)

Inhaltsverzeichnis

Kapitel 10: Bedingungen