Zeiger und Zeigertypen (Delphi)

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Datentypen, Variablen und Konstanten - Index


Ein Zeiger ist eine Variable, die eine Speicheradresse angibt. Wenn ein Zeiger die Adresse einer anderen Variablen enthält, zeigt er auf die Position dieser Variablen im Speicher oder auf die Daten, die an dieser Position gespeichert sind. Bei einem Array oder einem anderen strukturierten Typ enthält der Zeiger die Adresse des ersten Elements der Struktur. Ist diese Adresse bereits vergeben, enthält der Zeiger die Adresse des ersten Elements.

Zeiger sind typisiert, d. h. sie geben die Art der Daten an, auf die sie zeigen. Der Allzwecktyp Pointer repräsentiert einen Zeiger auf beliebige Daten, während spezialisierte Zeigertypen nur auf bestimmte Datentypen zeigen. Der Typ PByte wird für alle Byte-Daten verwendet, die keine Zeichendaten sind.

Auf 32-Bit-Plattformen belegt ein Zeiger vier Byte Speicherplatz als 32-Bit-Adresse. Auf 64-Bit-Plattformen belegt ein Zeiger acht Byte Speicherplatz als 64-Bit-Adresse.

Dieses Thema enthält Informationen zu folgenden Bereichen:

  • Allgemeiner Überblick über Zeigertypen
  • Deklarieren und Verwenden der von Delphi unterstützten Zeigertypen


Zeiger im Überblick

Das folgende Beispiel zeigt, wie Zeiger funktionieren:

 1         var
 2           X, Y: Integer;  // X and Y are Integer variables
 3           P: ^Integer;     // P points to an Integer
 4         begin
 5           X := 17;      // assign a value to X
 6           P := @X;      // assign the address of X to P
 7           Y := P^;      // dereference P; assign the result to Y
 8         end;

In Zeile 2 werden X und Y als Variablen des Typs Integer deklariert. Zeile 3 deklariert P als Zeiger auf einen Integer-Wert. P kann also auf die Position von X oder Y zeigen. In Zeile 5 wird X ein Wert zugewiesen. Zeile 6 weist P die Adresse von X (angegeben durch @X) zu. Schließlich wird in Zeile 7 der Wert an der Adresse ermittelt, auf die P zeigt (angegeben durch ^P), und Y zugewiesen. Nach der Ausführung dieses Programms haben X und Y denselben Wert (17).

Der Operator @ wird hier verwendet, um die Adresse einer Variablen zu ermitteln. Sie können diesen Operator aber auch für Funktionen und Prozeduren einsetzen. Weitere Informationen hierzu finden Sie unter Der Operator @ und Prozedurale Typen in Anweisungen und Ausdrücken.

Wie das obige Beispiel zeigt, erfüllt das Caret-Symbol ^ zwei Funktionen. Es kann vor einem Typbezeichner stehen. Beispiel:

^typeName

In diesem Fall bezeichnet das Caret-Symbol einen Typ, der Zeiger auf Variablen des Typs Typname darstellt.

Das Symbol kann aber auch auf eine Zeiger-Variable folgen:

pointer^

In diesem Fall dereferenziert das Caret-Symbol den Zeiger, liefert also den Wert an der Speicheradresse, die der Zeiger angibt.

Das obige Beispiel sieht auf den ersten Blick wie eine etwas umständliche Möglichkeit aus, den Wert einer Variablen in eine andere zu kopieren. Dies ließe sich viel einfacher durch eine entsprechende Zuweisung erreichen. Die Verwendung von Zeigern ist aber aus mehreren Gründen sinnvoll. Sie sind für das Verständnis der Sprache Delphi wichtig, da sie in einem Programm oft hinter den Kulissen agieren und nicht explizit auftreten. Zeiger werden von allen Datentypen verwendet, die große, dynamisch zugewiesene Speicherblöcke benötigen. Beispielsweise sind lange String-Variablen ebenso wie Klasseninstanzvariablen implizite Zeiger. In vielen komplexen Programmierkonstrukten ist die Verwendung von Zeigern unverzichtbar.

In vielen Situationen sind Zeiger die einzige Möglichkeit, die strikte Typisierung der Daten durch Delphi zu umgehen. Sie können z. B. die in einer Variablen gespeicherten Daten ohne Berücksichtigung ihres Typs verarbeiten, indem Sie sie über den Allzweckzeiger Pointer referenzieren, diesen in den gewünschten Typ umwandeln und ihn anschließend wieder dereferenzieren. Hier ein Beispiel, in dem die in einer reellen Variable gespeicherten Daten an eine Integer-Variable zugewiesen werden:

type
  PInteger = ^Integer;
var
  R: Single;
  I: Integer;
  P: Pointer;
  PI: PInteger;
begin
  ...
  P := @R;
  PI := PInteger(P);
  I := PI^;
end;

Reelle und Integer-Werte werden natürlich in unterschiedlichen Formaten gespeichert. Durch die Zuweisung werden lediglich die binären Rohdaten von R nach I kopiert. Eine Konvertierung findet dabei nicht statt.

Neben der Zuweisung des Ergebnisses einer @-Operation können aber auch diverse Standardroutinen eingesetzt werden, um einem Zeiger einen Wert zuzuweisen. Mit den Prozeduren New und GetMem lässt sich z.B. einem vorhandenen Zeiger eine Speicheradresse zuweisen. Die Funktionen Addr und Ptr geben einen Zeiger auf eine bestimmte Adresse oder Variable zurück.

Dereferenzierte Zeiger können qualifiziert werden und als Qualifizierer agieren. Ein Beispiel hierfür ist der Ausdruck P1^.Data^.

Das reservierte Wort nil ist eine spezielle Konstante, die jedem Zeiger zugewiesen werden kann. Ein solcher Zeiger verweist dann auf kein bestimmtes Ziel.

Erweiterte Syntax mit Zeigern

Die Compiler-Direktive {$EXTENDED} wirkt sich auf die Verwendung des Caret-Zeichens (^) aus. Wenn {$X+} aktiv ist (Vorgabe), können Sie beim Referenzieren von Zeigern das Caret weglassen. Das Caret ist aber beim Deklarieren eines Zeigers und für das Auflösen der Mehrdeutigkeit, wenn ein Zeiger auf einen anderen Zeiger zeigt, erforderlich. Weitere Informationen finden Sie unter Erweiterte Syntax (Delphi).

Wenn die erweiterte Syntax aktiviert ist, können Sie beim Referenzieren eines Zeigers das Caret-Zeichen weglassen:

{$X+}
 type
   PMyRec = ^TMyRec;
   TMyRec = record
     Data: Integer;
   end;

 var
   MyRec: PMyRec;

 begin
   New(MyRec);
   MyRec.Data := 42;  {#1}
 end.

Wenn die erweiterte Syntax nicht aktiviert ist, müsste die mit {#1} gekennzeichnete Zeile folgendermaßen lauten:

 MyRec^.Data := 42;

Zeigertypen

Mit der folgenden Syntax kann ein Zeiger auf jeden beliebigen Typ deklariert werden:

type  pointerTypeName = ^type

Bei der Definition eines Record-Typs oder eines anderen Datentyps könnte es hilfreich sein, auch einen Zeiger auf diesen Typ zu deklarieren. Dies erleichtert die Verarbeitung von Instanzen des Typs, da das Kopieren größerer Speicherblöcke entfällt.

Hinweis: Sie können einen Zeigertyp definieren, bevor Sie den Typ deklarieren, auf den er zeigt.

Es gibt Standardzeigertypen für unterschiedliche Verwendungszwecke. Mit dem Allzwecktyp Pointer kann jeder Datentyp referenziert werden. Eine Dereferenzierung von Variablen des Typs Pointer ist nicht möglich. Den Versuch, einer Pointer-Variablen das Symbol ^ nachzustellen, weist der Compiler zurück. Wenn Sie auf Daten zugreifen wollen, auf die eine Pointer-Variable zeigt, müssen Sie diese Variable in einen anderen Zeigertyp umwandeln, bevor Sie sie dereferenzieren.

Zeiger auf Zeichen

Die beiden fundamentalen Zeigertypen PAnsiChar und PWideChar stellen Zeiger auf AnsiChar- bzw. WideChar-Werte dar. Der generische Typ PChar repräsentiert einen Zeiger auf einen Char-Wert (in der aktuellen Implementierung auf einen WideChar-Wert). Diese Zeigertypen auf Zeichen werden zur Verarbeitung von nullterminierten Strings eingesetzt. Informationen hierzu finden Sie unter Nullterminierte Strings.

Hinweis: Wandeln Sie den Typ von Nicht-Zeichen-Zeigertypen nicht in PByte für die Zeigerarithmetik um. Verwenden Sie stattdessen den Zeigertyp {$POINTERMATH ON} deklariert wird.

Byte-Zeiger

Der fundamentale Typ PByte repräsentiert einen Zeiger auf alle Byte-Daten, die keine Zeichendaten sind. Dieser Typ wird mit der Compiler-Direktive {$POINTERMATH ON} deklariert.

function TCustomVirtualStringTree.InternalData(Node: PVirtualNode): Pointer;
begin
  if (Node = FRoot) or (Node = nil) then
    Result := nil
  else
    Result := PByte(Node) + FInternalDataOffset;
end;

Typgeprüfte Zeiger

Die Compiler-Direktive $T steuert die Typen der Zeigerwerte, die durch den Operator @ generiert werden. Diese Direktive hat das folgende Format:

{$T+} or {$T-}

Im Status {$T-} ist der Ergebnistyp des Operators @ immer ein untypisierter Zeiger, der mit allen anderen Zeigertypen kompatibel ist. Wird @ auf eine Variablenreferenz im Status {$T+} zugewiesen, lautet der Typ des Ergebnisses ^T, wobei T nur mit den Zeigern auf den Typ der Variable kompatibel ist.

Weitere Standardzeigertypen

Viele der häufig verwendeten Standardzeigertypen sind in den Units System und SysUtils deklariert.

Verwenden Sie die Direktive {POINTERMATH <ON|OFF>}, um die Zeigerarithmetik für alle typisierten Zeiger zu aktivieren oder zu deaktivieren, damit das Inkrementieren/Dekrementieren nach Elementgröße ausgeführt wird.

Eine Auswahl der in System und SysUtils deklarierten Zeigertypen:

Zeigertyp Zeigt auf Variablen des Typs

PString

UnicodeString

PAnsiString

AnsiString

PByteArray

TByteArray (deklariert in SysUtils). Wird für die Typumwandlung dynamisch verwalteter Speicherblöcke verwendet, um Array-Zugriffe zu ermöglichen.

PCurrency, PDouble, PExtended, PSingle

Currency, Double, Extended, Single

PInteger

Integer

POleVariant

OleVariant

PShortString

ShortString. Ist bei der Portierung von veraltetem Code hilfreich, in dem noch der Typ PString verwendet wird.

PTextBuf

TTextBuf (deklariert in SysUtils). TTextBuf ist der interne Puffer für den Datei-Record TTextRec.

PVarRec

TVarRec (deklariert in System)

PVariant

Variant

PWideString

WideString

PWordArray

TWordArray (deklariert in SysUtils). Wird für die Typumwandlung dynamisch verwalteter Speicherblöcke verwendet, wenn diese als Array mit 2-Byte-Werten bearbeitet werden sollen.

Siehe auch