Loading AI tools
Datentyp von Speicheradressen Aus Wikipedia, der freien Enzyklopädie
Mit Zeiger (englisch pointer) wird in der Informatik ein Objekt einer Programmiersprache bezeichnet, das eine Speicheradresse zwischenspeichert.
Der Zeiger referenziert (verweist, zeigt auf) einen Ort im Hauptspeicher des Computers. Hier können Variablen, Objekte oder Programmanweisungen gespeichert sein. Das Erlangen der dort hinterlegten Daten wird als dereferenzieren oder rückverweisen bezeichnet; siehe #Zeigeroperationen.
Ein Zeiger ist ein Sonderfall und in einigen Programmiersprachen die einzige Implementierungsmöglichkeit des Konzepts einer Referenz. Zeiger werden in diesem Sinne auch als Referenzvariable bezeichnet.
Zeiger werden unter anderem dazu verwendet, den dynamischen Speicher zu verwalten. So werden bestimmte Datenstrukturen, zum Beispiel verkettete Listen, in der Regel mit Hilfe von Zeigern implementiert.
Der Zeiger wurde 1964/65 von Harold Lawson eingeführt und in der Programmiersprache PL/I implementiert.
Zeiger kommen vor allem in maschinennahen Programmiersprachen, wie Assembler oder in Sprachen, die zur Systemprogrammierung geeignet sind, wie C oder C++, vor. Der Gebrauch in streng typisierten Sprachen wie Modula-2 oder Ada ist stark eingeschränkt und in Sprachen wie Java oder Eiffel zwar intern vorhanden, aber für den Programmierer verborgen (opak). 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. Bei Programmen, die in C oder C++ geschrieben sind, stellen sie häufige Ursachen für Pufferüberläufe oder Speicherzugriffsverletzungen und daraus folgende Abstürze oder Sicherheitslücken dar.[1]
In objektorientierten Sprachen tritt an die Stelle der Zeiger alternativ (C++) oder ausschließlich (Java, Python) die Referenz, die im Gegensatz zu Zeigern nicht ausdrücklich dereferenziert werden muss.
In der Sprache C# oder Visual Basic .NET kommen Zeiger im Grunde nicht vor. Alle Funktionalitäten, die Zeiger bieten, wurden durch Konzepte wie Delegate ersetzt. Es ist jedoch in C#, nicht aber in VB.NET möglich, unsicheren Code zu deklarieren (der auch speziell kompiliert werden muss), um Zeiger wie in C++ nutzen zu können.[2] Damit kann in manchen Fällen bessere Leistung erreicht werden.
Der Datentyp, auf den ein Zeiger verweist, wird als Verweistyp bezeichnet. Zeigertypen können nur innerhalb eines Blocks hinter dem Schlüsselwort unsafe
verwendet werden. Nur bestimmte Datentypen, nämlich elementare Datentypen, Aufzählungstypen, Zeigertypen und Strukturtypen können ein Verweistyp sein. Es ist möglich, Konvertierungen zwischen verschiedenen Zeigertypen sowie zwischen Zeigertypen und ganzzahligen Datentypen durchzuführen.
Die Typisierung eines Zeigers sagt aus, ob der Datentyp des Zeigers selbst Auskunft über den Datentyp des Wertes, auf den der Zeiger verweist, gibt. Es wird zwischen typisierten und untypisierten Zeigern unterschieden.[3]
Typisierte Zeiger können nur auf Werte des entsprechenden Datentyps verweisen. So kann ein „Zeiger auf ein Wert vom Typ Integer“ auch nur auf ein Wert vom Typ „Integer“ verweisen. Der Datentyp des Zeigers selbst bestimmt sich also durch den Datentyp, 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.[3] Darüber hinaus ermöglicht die Typisierung von Zeigern dem Compiler, Verletzungen der Typkompatibilität zu erkennen.[4]
Untypisierte Zeiger 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, C++ und D, in Objective-C vom Typ id
oder POINTER
in Pascal. In höheren Programmiersprachen existieren zum Teil keine untypisierten Zeiger.[3]
Der Nullzeiger ist ein spezieller Wert (ein sog. Nullwert, nicht zwingend numerisch 0). Wird dieser Wert einer programmiersprachlich als Zeiger deklarierten Variablen zugewiesen, zeigt dies an, dass mit der Zeigervariablen auf „nichts“ verwiesen wird. Nullzeiger werden in fast allen Programmiersprachen sehr gerne verwendet, um eine „designierte Leerstelle“ zu kennzeichnen. Eine Ausnahme ist die Programmiersprache Rust.[5] Zum Beispiel wird eine einfach verkettete Liste meist so implementiert, dass dem Folgezeiger des letzten Elements der Wert des Nullzeigers gegeben wird, um auszudrücken, dass es kein weiteres Element gibt. Auf diese Weise lässt sich ein zusätzliches Feld, das das Ende der Liste zu bedeuten hätte, einsparen.
In Pascal und Object Pascal heißt der Nullzeiger beispielsweise nil
(lateinisch: „nichts“ oder Akronym für „not in list“). In C kennzeichnet das in der Standardbibliothek enthaltene Präprozessor-Makro NULL
den Nullzeiger und verdeckt die interne Repräsentation. In C++ heißt der Nullzeiger ebenfalls NULL
und ist als Makro für die numerische Null (0
) definiert.[6] Im neuen C++-Standard C++11 wurde die Konstante nullptr
eingeführt, die eine typsichere Unterscheidung zwischen 0 und dem Nullzeiger ermöglicht. Donald Knuth stellt den Nullzeiger mit dem Symbol dar,[7] diese Konvention wird auch von den Werkzeugen WEB und CWEB (ebenda) verwendet.
Das Dereferenzieren eines Nullzeigers ist meist nicht erlaubt. Je nach Programmiersprache und Betriebssystem führt es zu undefiniertem Verhalten oder einem Programmabbruch per Ausnahmebehandlung (englisch exception) bzw. Schutzverletzung.
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 (uninitialisierter Zeiger) oder wenn sie noch auf eine Speicheradresse verweist, die nicht mehr gültig ist (wilder Zeiger). Zeigt der Zeiger nicht auf eine gültige Speicheradresse, kann es wie beim Nullzeiger zu einer Schutzverletzung kommen.
Als Intelligente Zeiger (engl. 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 alloziertes Speicherobjekt freigeben, sobald die letzte Referenz darauf gelöscht wird.
Zeiger auf eine COM- oder CORBA-Schnittstelle sind in manchen Programmiersprachen (z. B. Object Pascal) 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.
In C++ ist es möglich, analog zu Methodenzeigern auch Zeiger auf die Datenmember einer Klasse zu definieren:
#include <cstdint>
#include <vector>
using namespace std;
struct RGB_Pixel {
uint8_t red = 0, green = 0, blue = 128;
};
// definiert Typalias 'Channel' als Zeiger auf uint8_t-Datenmember der Klasse RGB_Pixel
using Channel = uint8_t RGB_Pixel::*;
// invertiert den ausgewählten RGB-Kanal aller Pixel eines Bildes
void invert(vector<RGB_Pixel>& image, Channel channel) {
for(RGB_Pixel& pixel: image)
pixel.*channel = 255 - pixel.*channel;
}
int main() {
vector<RGB_Pixel> image;
// Memberzeiger zeigt auf den grünen RGB-Kanal
Channel green = &RGB_Pixel::green;
// nur der grüne RGB-Kanal wird invertiert
invert(image, green);
}
Das Rechnen mit Zeigern auf Basis der Speichergröße des referenzierten Typs wird als Zeigerarithmetik bezeichnet. Dabei können Zeiger erhöht, verringert und subtrahiert werden. Alle Operationen berücksichtigen dabei die Größe des referenzierten Typs. Inkrementiert man beispielsweise den Zeiger auf einen vier Byte großen Datentyp um eins, so wird dessen Wert (also die Speicheradresse) um vier Bytes erhöht. Beim Subtrahieren zweier Zeiger erhält man die Anzahl dazwischen passender Objekte des referenzierten Typs.
In Sprachen wie C wird die Zeigerarithmetik üblicherweise eingesetzt, um über terminierte Arrays zu iterieren, wie in folgendem Programm, das über das Array seiner Argumente iteriert:
#include <stdio.h>
int
main(int argc, char *argv[]) /* argv ist NULL-terminiert */
{
char **cpp;
for (cpp = argv; *cpp; cpp++) { /* cpp wird jeweils um die Größe von char* erhöht */
printf("arg: %s -- adr: %p\n", *cpp, cpp); /* dereferenzierter Wert und seine Adresse */
}
printf("args: %lu\n", cpp - argv); /* Anzahl der Elemente im Array */
return 0;
}
Der Aufruf mit Ausgabe sieht auf einem 64-Bit-System beispielsweise so aus:
$ ./b foo bar arg: ./b -- adr: 0x7fffffffea00 arg: foo -- adr: 0x7fffffffea08 arg: bar -- adr: 0x7fffffffea10 args: 3
Da Zeigerarithmetik als fehleranfällig angesehen wird, wird sie in höheren Programmiersprachen meist nicht unterstützt, wobei dort andere Möglichkeiten gegeben sind, um die gleiche Funktionalität zu erlangen.
Man kann in C und C++ einer Zeigervariablen direkt ein Array zuweisen. Das Ergebnis ist, dass der Zeiger auf das erste Element des Arrays zeigt.
int triangleNumbers[5];
int* numbersPointer = triangleNumbers;
Über diese Zeigervariablen kann man über den Index-Operator auf die einzelnen Elemente des Arrays zugreifen:
numbersPointer[0] = 0;
numbersPointer[1] = 1;
numbersPointer[2] = 3;
numbersPointer[3] = 6;
numbersPointer[4] = 10;
cout << *numbersPointer << endl;
cout << numbersPointer[3] << endl;
In diesem Beispiel werden den Elementen des Arrays die ersten fünf Dreieckszahlen zugewiesen. Mit der vorletzten Anweisung wird der Inhalt des ersten Elements des Arrays, also 0, ausgegeben. Mit der letzten Anweisung wird der Inhalt des Array-Elements mit dem Index 3, also 6, ausgegeben.
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:
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.