Mit Zeiger oder Pointer wird in der Informatik eine spezielle Variable bezeichnet, die auf eine andere Variable oder Funktion verweist. Der referenzierte Speicherbereich enthält entweder Daten (Objekt, Variable) oder Programmcode. Zeiger auf Programmcode werden meist als Funktionszeiger bezeichnet.
Man unterscheidet bei Zeigern zwischen zwei Zugriffsverfahren:
Mit dem Begriff Zeiger wird fälschlicherweise häufig auch der Datentyp des Zeigers bezeichnet, hier muss allerdings richtig vom Zeigertyp gesprochen werden.
Ein verbreitetes Anwendungsgebiet für Zeiger ist die Referenzierung dynamisch angeforderten Speichers. Auch bestimmte Datenstrukturen, z. B. verkettete Listen, werden in der Regel mit Hilfe von Zeigern implementiert.
Ein Zeiger ist ein Spezialfall und in niedrigeren Programmiersprachen die einzige Implementierungsmöglichkeit des Konzepts einer Referenz.
Inhaltsverzeichnis |
Zeiger kommen vor allem in maschinennahen Programmiersprachen wie z. B. Assembler, C oder C++ vor, während man den Gebrauch in streng typisierten Sprachen wie Modula-2 oder Ada stark einschränkt und sie in Sprachen wie Java oder Eiffel zwar intern vorhanden, aber für den Programmierer vollständig verborgen (opak) sind. Mit erstgenannten Sprachen ist es möglich, Zeiger auf beliebige Stellen im Speicher zu erzeugen oder mit ihnen zu rechnen.
Manche Programmiersprachen schränken den Gebrauch von Zeigern ein, weil Programmierern bei der Arbeit mit Zeigern leicht schwerwiegende Programmierfehler unterlaufen (die so eine Ursache für Pufferüberläufe und Abstürze bei zum Beispiel in C und C++ geschriebenen Programmen darstellen).
In objektorientierten Sprachen tritt an die Stelle der Zeiger alternativ (C++) oder ausschließlich (Java, Python) die Referenz.
In der typsicheren Sprache C# kommen Zeiger „im Grunde nach“ nicht vor. Alle Funktionalitäten, die Zeiger bieten, wurden durch sichere Konzepte wie Delegate ersetzt. Es ist jedoch möglich, unsicheren Code zu deklarieren (der auch speziell kompiliert werden muss), um Zeiger wie in C++ nutzen zu können.[1] Damit kann in manchen Fällen bessere Performance erreicht werden oder es wird möglich auf die Windows-API-Funktionen zuzugreifen. Der Gebrauch ist jedoch stark abzuraten. Innerhalb der .NET-Framework stellt unsicherer Code ein Sicherheitsrisiko dar, weil dieser angelegte Speicher nicht vom Garbage Collector bereinigt werden kann und weil unsicherer Code von der Common Language Runtime generell nicht überprüft wird.
In den meisten Programmiersprachen werden Zeiger direkt mit Datentypen assoziiert. So kann ein so genannter "Zeiger auf ein Objekt vom Typ Integer" normalerweise auch nur auf ein Objekt vom Typ "Integer" verweisen. Der Datentyp des Zeigers selbst bestimmt sich also durch den Typ, auf den er verweist. In der Programmiersprache C ist dies eine Voraussetzung zur Realisierung der Zeigerarithmetik (s. u.), denn nur durch das Wissen um die Speichergröße des assoziierten Typs kann die Adresse des Vorgänger- oder Nachfolgeelementes berechnet werden. Darüber hinaus ermöglicht die Typisierung von Zeigern dem Compiler, Verletzungen der Typkompatibilität zu erkennen.
Diese Zeiger sind mit keinem Datentyp verbunden. Sie können nicht dereferenziert, inkrementiert oder dekrementiert werden, sondern müssen vor dem Zugriff in einen typisierten Zeigertyp umgewandelt werden.
Beispiele dafür sind der Typ void* in C und C++, in Objective-C vom Typ id oder POINTER in Pascal.
In höheren Programmiersprachen existieren zum Teil keine untypisierten Zeiger.
Der Nullzeiger ist ein Zeiger mit einem speziellen, dafür reservierten Wert (sog. Nullwert, nicht zwingend numerisch 0), der anzeigt, dass auf nichts verwiesen wird. Nullzeiger werden in fast allen Sprachen sehr häufig verwendet, da man mittels des Nullzeigers eine "designierte Leerstelle" kennzeichnet. Zum Beispiel wird eine einfach verkettete Liste meist so implementiert, dass das letzte Element auf den Nullzeiger als Folgeelement verweist. Er kennzeichnet in diesem Fall also das Ende der Liste. In Pascal-basierten Sprachen wie Delphi bzw. Object Pascal heißt der Nullzeiger beispielsweise nil (lateinisch: "nichts" oder Akronym für "not in list"), in C und in C++ die Zahl 0. Die C-Standardbibliothek definiert für diesen Zweck den speziellen Ausdruck NULL als Nullzeiger. Die Nullreferenz in Python heißt None.
Intern werden Nullzeiger auf unterschiedliche Arten repräsentiert, weshalb man sich nach Möglichkeit nie um den tatsächlichen Wert kümmert, sondern ihn einfach als Indikator benutzt, dass der Zeiger auf keinen benutzbaren Inhalt verweist. Die logische Folge ist, dass ein Nullzeiger nicht dereferenziert werden darf. Ein solcher Umgang mit dem Nullzeiger kann undefiniertes Verhalten hervorrufen. Auf vielen modernen Betriebssystemen wird das Programm mit einer Schutzverletzung abgebrochen.
Falls eine Zeigervariable dereferenziert wird, die nicht auf einen gültigen Speicherbereich des entsprechenden Typs zeigt, kann es ebenfalls zu unerwartetem Verhalten kommen. So kann eine Situation auftreten, wenn eine Variable vor ihrer Benutzung nicht auf einen gültigen Wert initialisiert wurde, oder wenn sie noch auf eine Speicheradresse verweist, die nicht mehr gültig ist (wilder Zeiger). Zeigt der Zeiger nicht auf eine gültige Speicheraddresse, kann es wie beim Nullzeiger zu einer Schutzverletzung kommen.
Das Erhöhen oder Erniedrigen eines Zeigers um einen festen Wert oder das Subtrahieren zweier Zeiger wird als Zeigerarithmetik bezeichnet.
Da diese Operationen sehr fehleranfällig sind, werden sie in höheren Programmiersprachen meist nicht unterstützt, wobei selbstverständlich dort wiederum andere Methoden gegeben sind, um die gleiche Funktionalität zu implementieren.
Die Verwendung von Zeigern kann in bestimmten Fällen den Programmablauf beschleunigen oder helfen, Speicherplatz zu sparen:
Es gibt Sprachen, die bewusst auf den Einsatz von Zeigern verzichten (s. o.). Dies hat vor allem folgende Gründe:
Als Intelligente Zeiger (smart pointers) werden Objekte bezeichnet, die einfache Zeiger einkapseln und mit zusätzlichen Funktionen und Eigenschaften ausstatten. Z. B. könnte ein "smart pointer" ein dynamisch alloziiertes Speicherobjekt freigeben, sobald die letzte Referenz darauf gelöscht wird.
Zeiger auf eine COM- oder CORBA-Schnittstelle sind in manchen Programmiersprachen (z. B. Delphi) als Intelligenter Zeiger implementiert.
Funktionszeiger bilden eine besondere Klasse von Zeigern. Sie zeigen nicht auf einen Bereich im Datensegment, sondern auf den Einsprungspunkt einer Funktion im Codesegment des Speichers. Damit ist es möglich, benutzerdefinierte Funktionsaufrufe, deren Ziel erst zur Laufzeit bestimmt wird, zu realisieren. Funktionszeiger kommen häufig in Verbindung mit Rückruffunktionen (callback function) zum Einsatz und stellen eine Form der späten Bindung dar.
Siehe auch: Methodenzeiger