Zeiger
Zeiger (engl.: pointer) enthalten eine Speicheradresse an der Daten des vorgegebenen Typs erwartet werden. Mit ihnen wird direkt auf einen Ort im Speicher verwiesen. Der Typ des Zeigers gibt ausserdem Aufschluss auf die zu erwartende Speichermenge, die ein Datum an dieser Stelle im Speicher benötigt.
In C müssen Zeiger mit dem '*-Operator dereferenziert werden, damit auf die Daten, auf die der Zeiger zeigt, zugegriffen werden kann. Der '&'-Operator liefert die Adresse des Elements zurück auf das der Operator angewandt wird.
int *ptr = (int *) 0x1100; /* reserviere Speicher für einen Zeiger des
* Typs 'int', der auf die Adresse '0x1100'
* im RAM zeigt. (int *) konvertiert dabei
* die Integerzahl 0x1100 in eine Adresse.
*/
int a; /* reserviere Speicher für einen Integerwert */
*ptr = 1234; /* schreibe den Wert '1234' an die Adresse,
* auf die 'ptr' zeigt
*/
a = *ptr; /* kopiere den Wert, auf den 'ptr' zeigt,
* in 'a'
*/
ptr = &a; /* kopiere die Adresse von 'a' in ptr */
- a enthält danach den Wert auf den ptr zeigt.
- ptr enthält danach die Adresse an welcher Speicher für a reserviert wurde.
Zeiger auf Zeiger
Zeiger auf Zeiger verhalten sich idenentisch zu Zeigern auf Daten. Wird ein solcher Zeiger dereferenziert, so erhält man Zugriff auf einen weiteren Zeiger. Wird dieser ebenfalls dereferenziert, kann der Wert der Variable gelesen oder geschrieben werden.
int WertA;
int * ZeigerAufWert = &WertA;
int ** ZeigerAufZeiger = &ZeigerAufWert;
**ZeigerAufZeiger = 42; /* WertA hat nun den Inhalt 42 */
int WertB;
*ZeigerAufZeiger = &WertB; /* ZeigerAufWert zeigt nun auf WertB */
Zeiger als Übergabewert
In C/C++ können beliebige Zeiger als Argument an eine Funktion übergeben werden:
void foo(int * Data); /* Zeiger auf einen int */
void bar(const int * Data); /* Zeiger auf einen const int */
Einen Zeiger auf const zu übergeben ist üblich, wenn die Funktion den Inhalt der Variable, auf die der Zeiger zeigt, nicht verändern wird. Wird kein solcher Zeiger übergeben, muss der Aufrufer damit rechnen, dass die Funktion den Wert ändert und muss somit eine Kopie anlegen, wenn der alte Wert später noch benötigt wird. Wird ein Zeiger auf ein Array übergeben, so empfielt es sich, die Grösse des Arrays ebenfalls an die Funktion zu übergeben (Ausnahme: Strings, siehe String-Verarbeitung_in_C):
void foo(int * Data, size_t DataSize);
Anders kann die Grösse nicht an eine Funktion übergeben werden, folgende Prototypen sind identisch:
void bar(int * Data);
void bar(int Data[]);
void bar(int Data[8]);
Soll eine Struktur oder Union an eine Funktion übergeben werden, wird dies üblicherweise mit Zeigern erledigt. Werden Strukturen by-value übergeben, müssen diese bei jedem Funktionsaufruf kopiert werden, was unnötig Arbeitsspeicher und Zeit kostet.
typedef struct{
int A, B;
}my_struct;
void foo(my_struct * Struktur){
Struktur->A = 42;
}
Wird innerhalb der Funktion eine Kopie benötigt, wird diese normalerweise auch innerhalb der Funktion erstellt und nicht by-value übergeben. Sollte das Kopieren der Struktur nach einer Änderung nicht mehr nötig sein, so reicht es, nur die Funktion selbst zu ändern. Der Funktionsprototyp in der Headerdatei bleibt gleich. Damit ist ausgeschlossen, dass an anderen Stellen der Software ebenfalls Änderungen vorgenommen werden müssen.
Zeiger auf void
Ein Zeiger auf void kann in C dann nützlich sein, wenn der endgültige Datentyp bei der Erstellung einer Bibliothek unbekannt ist. Ein Zeiger auf void kann auf einen beliebigen Datentyp zeigen. Die Funktion malloc aus stdlib.h nutzt diese Eigenschaft, um Speicher vom Betriebssystem anzufordern. Der von malloc zurückgegebene Zeiger wird anschliessend automatisch in einen 'normalen' Zeiger umgewandelt.
int * Zeiger = malloc(123 * sizeof(*ZeigerA));
Auch die Umwandlung eines 'normalen' Zeigers in einen Zeiger auf void geschieht in C automatisch:
free(Zeiger);
In C++ wurde diese automatische Umwandlung abgeschafft. Daher ist es meist ein schlechtes Zeichen, wenn ein Zeiger auf void mit einem Cast umgewandelt werden muss. Die Verwendung eine Zeigers auf void ist in C++ im Allgemeinen nicht nötig, da die Sprache Templates und Überladen von Operatoren beherrscht. Mit diesen können alle Dinge erledigt werden, bei denen in C ein Zeiger auf void verwendet wird.
Siehe auch
Weblinks
- ThreeStarProgrammer: Warum man keinen hochkomplexen Code schreiben soll (engl.)