Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

Pointer und Funktionszeiger in C: Universelles Sortieren mit generischen Algorithmen

Lerne, wie du in C mit Pointern und Funktionszeigern eine generische Sortierfunktion schreibst, die für beliebige Datentypen funktioniert – inklusive Praxisbeispielen und Makefile-Tipps.

Pointer C Funktionszeiger C generisches Sortieren C void pointer C C Programmierung Tutorial CSCI 247 Lab 4 Lösung sort_array Funktion Funktionszeiger Syntax Makefile C Sortieralgorithmen C struct sortieren C C Vergleichsfunktion C generische Algorithmen C Pointer Beispiel C Programmierung Studium C Lab Übung

Einführung: Warum Pointer und Funktionszeiger?

In der C-Programmierung sind Pointer ein mächtiges Werkzeug, um Speicher effizient zu verwalten und flexible Algorithmen zu schreiben. Besonders spannend wird es, wenn du Funktionszeiger einsetzt, um generische Funktionen zu bauen – wie eine universelle Sortierroutine, die mit verschiedenen Datentypen arbeitet. Dieses Tutorial führt dich Schritt für Schritt durch die Konzepte, die du für das Lab 4 in CSCI 247 brauchst. Wir nutzen aktuelle Beispiele aus der Welt der KI-Apps und Finanzalgorithmen, um die Theorie greifbar zu machen.

Pointer auf void*: Der generische Datentyp

Ein void* ist ein Pointer auf einen unbekannten Typ. Stell dir vor, du hast ein Array von Strings, Zahlen oder sogar Strukturen – die Adressen der Elemente sind alle void*. Das Problem: Du kannst void* nicht direkt vergleichen, denn du weißt nicht, worauf sie zeigen. Ein Vergleich der Adressen selbst (z.B. Array[i] < Array[i+1]) sortiert nach Speicherorten, nicht nach Inhalten. Das ist so sinnvoll, wie eine Playlist nach Song-ID statt nach Titel zu sortieren – chaotisch!

Stell dir vor, du hast eine KI-gestützte Musik-App, die Songs nach Beliebtheit sortiert. Die Pointer zeigen auf die Song-Daten, aber die Sortierfunktion muss wissen, wie sie zwei Songs vergleicht – z.B. nach Anzahl der Streams. Ein generischer Algorithmus erwartet eine Vergleichsfunktion als Parameter.

Funktionszeiger: Der Schlüssel zur Generik

In C kannst du eine Funktion nicht direkt als Parameter übergeben, aber du kannst einen Pointer auf eine Funktion übergeben. Die Syntax ist gewöhnungsbedürftig: int (*ordered)(void*, void*) bedeutet: ordered ist ein Zeiger auf eine Funktion, die zwei void* entgegennimmt und einen int zurückgibt. Die Klammern um *ordered sind essenziell, sonst würde es als Funktion gedeutet, die einen int* zurückgibt.

// Deklaration einer Sortierfunktion mit Funktionszeiger
void sort_array(void* Array[], unsigned size, int (*ordered)(void*, void*));

Der Aufruf der Vergleichsfunktion innerhalb von sort_array ist dann einfach: if (ordered(Array[i], Array[i+1])). Die tatsächliche Vergleichslogik wird vom Aufrufer bereitgestellt. Das ist wie ein Algorithmus an der Börse: Die Sortierstrategie (z.B. nach Kurs oder Volumen) wird als Parameter übergeben, während die Sortierroutine selbst gleich bleibt.

Beispiel: Strings alphabetisch sortieren

Für ein Array von Strings (also char*) brauchst du eine Vergleichsfunktion, die strcmp nutzt:

static int ordered_strings(void* left, void* right) {
    char* left_str = (char*) left;
    char* right_str = (char*) right;
    return strcmp(left_str, right_str) < 0;
}

Beachte: Die Funktion erwartet void* und castet sie zu char*. Der Rückgabewert ist 1, wenn die linke Zeichenkette lexikografisch kleiner ist. Um die Sortierreihenfolge umzukehren, kannst du einfach das Vergleichsergebnis negieren (z.B. strcmp(...) > 0). Im Lab 4 wird genau das verlangt: Korrigiere die Sortierrichtung, sodass Strings aufsteigend sortiert werden.

Strukturen sortieren: Autos nach Baujahr

Ein häufiges Szenario in Datenbanken und Finanz-Apps ist das Sortieren von Strukturen. Angenommen, du hast ein Array von struct car mit den Feldern model und year. Die Elemente im Array sind Pointer auf diese Strukturen (struct car*). Deine Vergleichsfunktion muss die void* zu struct car* casten und das Jahr vergleichen:

static int ordered_cars(void* left, void* right) {
    struct car* left_car = (struct car*) left;
    struct car* right_car = (struct car*) right;
    return left_car->year < right_car->year;
}

Dann übergibst du diese Funktion an sort_array – und schon sortiert der generische Algorithmus Autos aufsteigend nach Baujahr. Das ist flexibel: Willst du nach Modell sortieren, schreibst du einfach eine andere Vergleichsfunktion.

Makefile und Kompilierung

Im Lab 4 wird ein Makefile bereitgestellt, das die Kompilierung vereinfacht. Mit make sort_strings baust du nur das String-Programm. Mit make clean entfernst du alle Objektdateien. Das ist typisch für größere C-Projekte, wie sie in der Spieleentwicklung oder bei KI-Frameworks vorkommen. Ein Tipp: Nutze make all, um alles zu bauen, aber achte darauf, dass deine Änderungen in sort.c und den Testdateien korrekt sind.

Häufige Fehler und Tipps

  • Falsche Casts: Vergiss nicht, die void* in der Vergleichsfunktion zu casten. Ein fehlender Cast führt zu Compiler-Warnungen oder falschen Ergebnissen.
  • Sortierrichtung: Im Lab 4 sollst du die Sortierung von Strings umkehren. Prüfe, ob strcmp das gewünschte Ergebnis liefert.
  • Funktionszeiger-Syntax: Die Klammern um *ordered sind Pflicht. Ein häufiger Fehler ist int *ordered(void*, void*) – das wäre eine Funktion, die einen Pointer zurückgibt.
  • Makefile-Ziele: Verwende make sort_strings und make sort_structs, um einzelne Programme zu testen. Mit make clean startest du frisch.

Fazit: Generisches Sortieren in der Praxis

Mit Funktionszeigern und void* kannst du in C extrem flexible Algorithmen schreiben. Das Konzept taucht in vielen Bereichen wieder auf: In KI-Bibliotheken wie TensorFlow werden Callback-Funktionen für Datenvorverarbeitung genutzt, in Finanzsoftware für Sortierungen von Kursen, und in Spiele-Engines für Prioritätswarteschlangen. Wenn du dieses Lab verstanden hast, bist du bereit für fortgeschrittene Themen wie generische Datenstrukturen (z.B. Binärbäume mit Vergleichsfunktion) oder Event-Handler in GUI-Programmen. Übe die Syntax und probiere verschiedene Vergleichsfunktionen aus – dann wird C zu einem mächtigen Werkzeug in deinem Programmier-Arsenal.