Ein Zeiger (pointer) ist eine Variable, die eine Speicheradresse enthält. Zeiger sind das Kernkonzept von C: Sie ermöglichen dynamischen Speicher, Datenstrukturen, effiziente Übergabe großer Daten und den Zugriff auf Hardware.
Adressen und Dereferenzierung
#include <stdio.h>
int main(void) {
int zahl = 42;
int *p = &zahl; // p speichert die Adresse von zahl
printf("Wert: %d, Zeiger zeigt auf: %d\n", zahl, *p);
*p = 100; // Wert über den Zeiger ändern
printf("zahl ist jetzt %d\n", zahl);
printf("gleiche Adresse: %d\n", p == &zahl);
printf("Größe des Zeigers: %zu Byte\n", sizeof p);
return 0;
}Wert: 42, Zeiger zeigt auf: 42 zahl ist jetzt 100 gleiche Adresse: 1 Größe des Zeigers: 8 Byte
| Schreibweise | Bedeutung |
|---|---|
int *p | p ist ein Zeiger auf int |
&x | Adresse von x |
*p | der Wert, auf den p zeigt (Dereferenzierung) |
NULL | "zeigt auf nichts" |
p->feld | Feld einer Struktur über einen Zeiger (Kurzform von (*p).feld) |
NULL
Ein Zeiger ohne Ziel setzt du auf NULL. Ein Zugriff auf NULL stürzt ab (Segmentation fault). Prüfe vor jedem Zugriff:
#include <stdio.h>
int *finde(int *a, int n, int gesucht) {
for (int i = 0; i < n; i++) if (a[i] == gesucht) return &a[i];
return NULL;
}
int main(void) {
int z[] = {5, 8, 12};
int *p = finde(z, 3, 8);
if (p) printf("gefunden: %d an Position %ld\n", *p, p - z);
int *q = finde(z, 3, 99);
printf("%s\n", q == NULL ? "nicht gefunden" : "gefunden");
return 0;
}gefunden: 8 an Position 1 nicht gefunden
Zeiger und Arrays
Ein Array-Name ist (fast überall) die Adresse seines ersten Elements. Mit Zeigern kannst du durch das Array wandern:
#include <stdio.h>
int main(void) {
int z[] = {10, 20, 30, 40};
int *p = z; // zeigt auf z[0]
printf("%d %d %d\n", *p, *(p + 1), p[2]); // *(p+i) ist dasselbe wie p[i]
p++; // um ein Element weiter (nicht ein Byte!)
printf("%d\n", *p);
int *ende = z + 4;
for (int *q = z; q < ende; q++) printf("%d ", *q);
printf("\n");
printf("%ld\n", ende - z); // Abstand in Elementen
return 0;
}10 20 30 20 10 20 30 40 4
Zeigerarithmetik rechnet in Elementgrößen: p + 1 springt bei int * um sizeof(int) Bytes.
const mit Zeigern
| Deklaration | Bedeutung |
|---|---|
const int *p | Wert nicht änderbar, Zeiger schon (häufigste Form für Parameter) |
int *const p | Zeiger nicht änderbar, Wert schon |
const int *const p | beides fest |
Lies von rechts nach links: const int *p = "p ist ein Zeiger auf ein konstantes int".
Zeiger auf Zeiger und Zeigerarrays
#include <stdio.h>
int main(void) {
const char *namen[] = {"Mia", "Tom", "Zoe"}; // Array von Zeigern auf Strings
for (int i = 0; i < 3; i++) printf("%s ", namen[i]);
printf("\n");
int x = 5;
int *p = &x;
int **pp = &p; // Zeiger auf Zeiger
**pp = 6;
printf("%d\n", x);
return 0;
}Mia Tom Zoe 6
argv in int main(int argc, char *argv[]) ist so ein Array von Strings (die Befehlszeilen-Argumente).
void-Zeiger
Ein void * kann auf alles zeigen, ohne Typ. Vor dem Benutzen wandelst du ihn in den richtigen Typ um. malloc und qsort benutzen ihn:
#include <stdio.h>
#include <string.h>
void zeige(const void *daten, size_t n) {
const unsigned char *b = daten; // Bytes ansehen
for (size_t i = 0; i < n; i++) printf("%02x ", b[i]);
printf("\n");
}
int main(void) {
unsigned int v = 0x01020304;
zeige(&v, sizeof v); // zeigt die Byte-Reihenfolge (Endianness)
char s[] = "Hi";
zeige(s, sizeof s);
return 0;
}04 03 02 01 48 69 00
Typische Zeigerfehler
| Fehler | Folge |
|---|---|
| Zeiger nicht initialisiert | zufällige Adresse: Absturz oder Datenfehler |
NULL dereferenzieren | Segmentation fault |
| Zeiger auf lokale Variable zurückgeben | Zeiger auf ungültigen Speicher (dangling pointer) |
| Zugriff außerhalb eines Arrays | undefiniertes Verhalten |
Zeiger nach free weiterbenutzen | use after free |
int *falsch(void) {
int lokal = 42;
return &lokal; // lokal existiert nach dem Return nicht mehr!
}Der Compiler warnt davor (-Wall: "function returns address of local variable").
Merke
- Ein Zeiger speichert eine Adresse:
&xholt sie,*pgreift darauf zu NULLheißt "nichts"; vor Zugriff prüfen- Array-Name ≈ Zeiger auf das erste Element;
p[i]ist*(p + i) - Zeigerarithmetik rechnet in Elementgrößen
const int *pschützt die Daten;void *ist ein typloser Zeiger- Nie Zeiger auf lokale Variablen zurückgeben
Aufgabe
Schreibe eine Funktion umkehren(int *a, int n), die ein Array mit zwei Zeigern (einer vorn, einer hinten) umkehrt.