Historisch gesehen sind Zeiger die Grundlage für die Arbeit mit Speicher in der C-Sprache und bieten einen flexiblen Mechanismus für den effektiven Zugriff auf die Elemente von Arrays und dynamischen Strukturen. Der Syntax und die Semantik von Zeigern auf Arrays und Arrays von Zeigern führen jedoch oft zu Verwirrung.
Problem: Anfängerprogrammierer verwechseln oft Zeiger auf Array (pointer to array) und Array von Zeigern (array of pointers), was zu falscher Nutzung des Speichers, Fehlern bei der Parameterübergabe und schwer zu konstruierenden Syntaxfehlern führt.
Lösung:
Beispiel für Deklaration und Nutzung:
// Zeiger auf ein Array von 10 int: int (*p)[10]; int arr[10]; p = &arr; // Array von 10 Zeigern auf int int *ap[10]; for (int i = 0; i < 10; ++i) { ap[i] = &arr[i]; } // Wie man ein Element durch einen Zeiger auf ein Array erhält: (*p)[2] = 5; // drittes Element von arr // Wie man einen Wert verwendet, indem man ein Array von Zeigern nutzt: *ap[2] = 8; // drittes Element von arr über ap
Schlüsselfunktionen:
**int p[10] und int (p)[10] — sind sie gleich?
Nein. int *p[10] ist ein Array von 10 Zeigern auf int. int (*p)[10] ist ein Zeiger auf ein Array von 10 int. Es gibt große Verwirrung ohne Klammern!
Beispielcode:
int arr[10]; int *p[10]; // Array von Zeigern int (*q)[10] = &arr; // Zeiger auf Array
*Kann man einen normalen Zeiger auf int einer Variable vom Typ int (p)[10] frei zuweisen?
Nein. Ein normaler int * zeigt auf ein einzelnes Element, während int (*p)[10] auf ein Array von 10 Ganzzahlen zeigt; die Typen sind ohne explizite Umwandlung nicht kompatibel.
Wie übergibt man ein zweidimensionales Array korrekt an eine Funktion?
Man muss die Größe der zweiten Dimension explizit angeben:
void foo(int a[][4], int n); // Array von n Zeilen mit 4 Elementen
oder einen Zeiger auf ein Array verwenden:
void bar(int (*a)[4], int n);
Ein Ingenieur deklariert eine Variable als int *p[10], versucht, &arr zuzuweisen, wo arr — int arr[10] ist und versucht, darauf als auf ein Array zuzugreifen, erhält einen Kompilierungsfehler oder nicht valides Verhalten.
Vorteile:
Nachteile:
Ein Entwickler verwendet vorsichtig Klammern: int (*p)[10], versteht deutlich den Unterschied, übergibt Arrays korrekt an Funktionen und nutzt typedef zur Vereinfachung von Deklarationen.
Vorteile:
Nachteile: