Zum Inhalt springen
C Anfรคnger 35 min

Pointer & Arrays

Das Herzstueck von C: Pointer, Adressen, Pointer-Arithmetik und wie Arrays und Pointer zusammenhaengen. Plus: malloc/free und Strings.

Aktualisiert:
Inhaltsverzeichnis

Pointer & Arrays

Hier kommt der Kern von C. Pointer machen die Sprache maechtig - und gleichzeitig gefaehrlich. Zeit, das Mysterium aufzuloesen.

Was ist ein Pointer?

Ein Pointer ist eine Variable, die eine Speicheradresse enthaelt. Statt eines Werts speicherst du โ€œwo ein Wert stehtโ€.

int zahl = 42;      // zahl ist 42, liegt an irgendeiner Adresse
int *p = &zahl;     // p haelt die Adresse von zahl

printf("Wert:    %d\n", zahl);    // 42
printf("Adresse: %p\n", (void*) &zahl);
printf("Via p:   %d\n", *p);       // 42 - dereferenzieren

Die Schluessel-Operatoren

  • & (Adressoperator): โ€œgib mir die Adresse vonโ€ฆโ€
  • * (Dereferenzierung): โ€œgib mir den Wert an der Adresseโ€ฆโ€
int x = 10;
int *p = &x;   // p zeigt auf x
*p = 20;       // aendert x auf 20 (ueber p)
printf("%d\n", x);  // 20

Typ-Mismatch pruefen

int *p;
double d = 3.14;
// p = &d;    // FEHLER: int* kann nicht auf double zeigen

NULL - der leere Pointer

int *p = NULL;   // p zeigt auf nichts

if (p != NULL) {
    printf("%d\n", *p);   // nur dereferenzieren, wenn nicht NULL!
}

Sehr wichtig: Einen NULL-Pointer zu dereferenzieren ist Undefined Behavior - typisch: Segmentation Fault.

Arrays und Pointer

In C sind Arrays und Pointer eng verwandt - der Array-Name ist ein Pointer auf das erste Element:

int zahlen[] = {10, 20, 30, 40, 50};
int *p = zahlen;       // gleich wie &zahlen[0]

printf("%d\n", *p);     // 10
printf("%d\n", p[0]);   // 10
printf("%d\n", zahlen[0]);  // 10

Pointer-Arithmetik

Du kannst Pointer inkrementieren - das bewegt sie um die Groesse des Typs weiter:

int zahlen[] = {10, 20, 30, 40, 50};
int *p = zahlen;

printf("%d\n", *p);      // 10
p++;
printf("%d\n", *p);      // 20
p += 2;
printf("%d\n", *p);      // 40

Oder direkt:

for (int *p = zahlen; p < zahlen + 5; p++) {
    printf("%d\n", *p);
}

Auch schoen:

printf("%d\n", *(zahlen + 2));   // 30
printf("%d\n", zahlen[2]);        // 30 - gleichwertig!

Der Trick: a[i] ist *(a + i)

Das ist die fundamentale Gleichung. Arrays sind Pointer mit Syntax-Zucker.

Arrays an Funktionen uebergeben

void ausgeben(const int *zahlen, size_t n) {
    for (size_t i = 0; i < n; i++) {
        printf("%d\n", zahlen[i]);
    }
}

int main(void) {
    int werte[] = {1, 2, 3, 4, 5};
    ausgeben(werte, 5);
    return 0;
}

const int * signalisiert: โ€œIch lese nur, aendere nichts.โ€

Achtung: sizeof bei Parameter

Innerhalb der Funktion liefert sizeof(zahlen) die Groesse eines Pointers, nicht des Arrays - weil der Parameter ein Pointer ist. Deshalb musst du die Groesse mitgeben.

Strings - nullterminierte Char-Arrays

char name[] = "Anna";
// gleich wie: char name[] = {'A', 'n', 'n', 'a', '\0'};

char *zeiger = "Anna";   // zeigt auf String-Literal (read-only!)

Unterschied: Array vs. Pointer

char arr[] = "Anna";      // Kopie im Stack, veraenderbar
char *ptr = "Anna";        // zeigt auf read-only Speicher

arr[0] = 'B';              // OK - "Bnna"
// ptr[0] = 'B';           // CRASH - Schreibzugriff auf Read-Only-Memory

Immer mit const arbeiten, wenn du String-Literale meinst:

const char *ptr = "Anna";

String-Funktionen aus <string.h>

#include <string.h>

char a[] = "Hallo";
char b[] = " Welt";

strlen(a);              // 5 (ohne \0)
strcmp(a, "Hallo");     // 0 (gleich)
strcpy(ziel, a);        // kopieren (gefaehrlich, besser strncpy)
strncpy(ziel, a, n);    // kopiere max. n Zeichen
strcat(ziel, b);        // anhaengen (gefaehrlich, besser strncat)

Wichtig: Strings in C sind eine haeufige Quelle fuer Buffer-Overflows. Immer darauf achten, dass der Zielpuffer gross genug ist.

Dynamische Speicherverwaltung

Bisher lag alles auf dem Stack. Fuer dynamische Daten brauchst du den Heap:

#include <stdlib.h>

int *zahlen = malloc(sizeof(int) * 100);

if (zahlen == NULL) {
    fprintf(stderr, "Speicher voll\n");
    return 1;
}

for (int i = 0; i < 100; i++) {
    zahlen[i] = i * i;
}

free(zahlen);   // PFLICHT - sonst Memory Leak!
zahlen = NULL;  // guter Stil - verhindert Versehen

Die Regeln

  1. Jedes malloc braucht genau ein free
  2. Nach free: Pointer nicht mehr nutzen
  3. malloc kann fehlschlagen - immer auf NULL pruefen
  4. Tool: AddressSanitizer (-fsanitize=address) oder Valgrind hilft Leaks zu finden

calloc und realloc

int *p = calloc(100, sizeof(int));   // 100 Ints, alle 0
int *groesser = realloc(p, sizeof(int) * 200);   // auf 200 erweitern

Pointer auf Pointer

Ja, das gibt es. Ein Pointer kann auf einen Pointer zeigen:

int x = 42;
int *p = &x;
int **pp = &p;

printf("%d\n", **pp);   // 42

Kommt vor bei:

  • Funktionen, die einen Pointer selbst veraendern sollen
  • 2D-Arrays (Arrays von Pointern auf Zeilen)
  • char *argv[] in main - ein Pointer auf mehrere Strings (die selbst Pointer sind)

Praktisches Beispiel

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char name[50];
    int alter;
} Person;

int main(void) {
    // Dynamisches Array von Personen
    size_t n = 3;
    Person *leute = malloc(sizeof(Person) * n);
    if (!leute) {
        fprintf(stderr, "Speicher voll\n");
        return 1;
    }

    strcpy(leute[0].name, "Max"); leute[0].alter = 34;
    strcpy(leute[1].name, "Anna"); leute[1].alter = 28;
    strcpy(leute[2].name, "Leo"); leute[2].alter = 22;

    for (size_t i = 0; i < n; i++) {
        printf("%s ist %d Jahre alt\n", leute[i].name, leute[i].alter);
    }

    free(leute);
    return 0;
}

Typische Fallen

Dangling Pointer

int *p = malloc(sizeof(int));
*p = 42;
free(p);
printf("%d\n", *p);   // UNDEFINED BEHAVIOR!

Use-After-Return

int *schlecht(void) {
    int x = 42;
    return &x;     // x lebt nur in dieser Funktion!
}

int main(void) {
    int *p = schlecht();
    printf("%d\n", *p);   // UNDEFINED BEHAVIOR!
}

Buffer Overflow

char puffer[10];
strcpy(puffer, "Das ist ein zu langer String");   // CRASH / Sicherheitsluecke

Besser:

strncpy(puffer, quelle, sizeof(puffer) - 1);
puffer[sizeof(puffer) - 1] = '\0';

Oder noch besser: Die sichereren Varianten der C Standard Library (C11+).

Zusammenfassung

  • Pointer sind Variablen, die Adressen halten
  • & holt die Adresse, * dereferenziert
  • Arrays sind Pointer - a[i] ist gleich *(a + i)
  • Strings in C sind nullterminierte Char-Arrays
  • malloc / free fuer den Heap - jedes malloc braucht genau ein free
  • AddressSanitizer hilft beim Finden von Memory-Bugs
  • Typische Fallen: Dangling Pointer, Buffer Overflow, Use-After-Free

Damit hast du das Fundament von C. Im weiteren Kurs vertiefen wir Structs, Bit-Manipulation, Dateioperationen und die Standardbibliothek.

Zurรผck zum C Kurs