Pointer & Arrays
Das Herzstueck von C: Pointer, Adressen, Pointer-Arithmetik und wie Arrays und Pointer zusammenhaengen. Plus: malloc/free und Strings.
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
- Jedes
mallocbraucht genau einfree - Nach
free: Pointer nicht mehr nutzen mallockann fehlschlagen - immer aufNULLpruefen- 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[]inmain- 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/freefuer 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.