EMZETT.
Login

Kurz: 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.

Teil des Kurses C

Kapitel 9 von 15 im Kurs C. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

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;
}

Ausgabe:

Wert: 42, Zeiger zeigt auf: 42
zahl ist jetzt 100
gleiche Adresse: 1
Größe des Zeigers: 8 Byte
SchreibweiseBedeutung
int *pp ist ein Zeiger auf int
&xAdresse von x
*pder Wert, auf den p zeigt (Dereferenzierung)
NULL”zeigt auf nichts”
p->feldFeld 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;
}

Ausgabe:

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;
}

Ausgabe:

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

DeklarationBedeutung
const int *pWert nicht änderbar, Zeiger schon (häufigste Form für Parameter)
int *const pZeiger nicht änderbar, Wert schon
const int *const pbeides 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;
}

Ausgabe:

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;
}

Ausgabe:

04 03 02 01
48 69 00

Typische Zeigerfehler

FehlerFolge
Zeiger nicht initialisiertzufällige Adresse: Absturz oder Datenfehler
NULL dereferenzierenSegmentation fault
Zeiger auf lokale Variable zurückgebenZeiger auf ungültigen Speicher (dangling pointer)
Zugriff außerhalb eines Arraysundefiniertes Verhalten
Zeiger nach free weiterbenutzenuse 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: &x holt sie, *p greift darauf zu
  • NULL heiß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 *p schützt die Daten; void * ist ein typloser Zeiger
  • Nie Zeiger auf lokale Variablen zurückgeben

Übungsaufgabe

Schreibe eine Funktion umkehren(int *a, int n), die ein Array mit zwei Zeigern (einer vorn, einer hinten) umkehrt.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Arrays und Strings

Weiter: Strukturen, Enums und Unions

Alle Kapitel: C im Überblick