Generisches Swap
Teil des Abschnitts Objektorientierte Programmierung der C-Journey von Coddy. Lektion 51 von 61.
Eine häufige Operation in der Programmierung ist das Vertauschen zweier Werte. Normalerweise würdest du für jeden Typ eine eigene Tauschfunktion schreiben: eine für int, eine andere für double und so weiter. Mit void* und memcpy können wir jedoch eine einzige Funktion schreiben, die zwei beliebige Variablen vertauscht.
Die wichtigste Erkenntnis ist, dass das Austauschen lediglich das Verschieben von Bytes ist. Wenn du die Größe der Daten kennst, kannst du Bytes kopieren, ohne den tatsächlichen Typ zu kennen. Die Funktionssignatur sieht folgendermaßen aus:
void generic_swap(void* a, void* b, size_t size);Der Parameter size teilt der Funktion mit, wie viele Bytes verschoben werden sollen. Im Inneren verwenden wir einen temporären Puffer und memcpy aus <string.h>, um die Bytes zu vertauschen:
void generic_swap(void* a, void* b, size_t size) {
char temp[size]; // temporärer Puffer
memcpy(temp, a, size); // temp = a
memcpy(a, b, size); // a = b
memcpy(b, temp, size); // b = temp
}Jetzt funktioniert diese einzelne Funktion für jeden Typ:
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x ist jetzt 10, y ist jetzt 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p ist jetzt 2.71, q ist jetzt 3.14Dieses Muster, bei dem void* mit size_t verwendet wird, ist die Vorgehensweise, mit der die Standardbibliothek von C generische Funktionen wie qsort und bsearch implementiert.
Aufgabe
EinfachErstellen wir ein generisches Dienstprogramm zum Tauschen, das die Werte beliebiger zweier Variablen unabhängig von ihrem Typ austauschen kann: mithilfe der Leistungsfähigkeit von void*-Zeigern und memcpy.
Du wirst deinen Code auf drei Dateien verteilen:
swap.h: Deklariere deinegeneric_swap-Funktion, die zweivoid*-Zeiger und einensize_t-Parameter entgegennimmt, der die Größe der zu tauschenden Daten angibt. Vergiss die Include-Guards nicht!swap.c: Implementiere diegeneric_swap-Funktion. Verwende einen temporären Puffer undmemcpy, um Bytes zwischen den beiden Speicherstellen zu verschieben. Denke an das Drei-Schritte-Muster zum Tauschen: den ersten Wert in die temporäre Variable kopieren, den zweiten in den ersten kopieren, die temporäre Variable in den zweiten kopieren.main.c: Zeige, dass dein generisches Tauschen mit verschiedenen Datentypen funktioniert. Lies einen Typindikator (ifür Ganzzahlen,dfür Gleitkommazahlen), gefolgt von zwei Werten. Erstelle zwei Variablen des passenden Typs, tausche sie mithilfe deinergeneric_swap-Funktion und gib anschließend beide Werte nach dem Tausch aus.
Dein Programm erhält drei Eingaben:
- Einen Typindikator:
ifür Ganzzahlen oderdfür Gleitkommazahlen - Den ersten Wert
- Den zweiten Wert
Gib nach dem Tausch beide Werte in separaten Zeilen aus. Verwende für Gleitkommazahlen 2 Nachkommastellen.
Beispielausgabe, wenn die Eingaben i, 5 und 10 sind:
10
5Beispielausgabe, wenn die Eingaben d, 3.14 und 2.71 sind:
2.71
3.14Beispielausgabe, wenn die Eingaben i, -7 und 42 sind:
42
-7Beispielausgabe, wenn die Eingaben d, 99.99 und 0.01 sind:
0.01
99.99Das Schöne an diesem Ansatz ist, dass deine generic_swap-Funktion keine Ahnung hat, welche Typen sie tauscht. Sie verschiebt lediglich Bytes. Dieselbe Funktion funktioniert für Ganzzahlen, Gleitkommazahlen, Strukturen oder jeden anderen Datentyp. Du musst <string.h> für memcpy und <stddef.h> oder <stdlib.h> für size_t einbinden.
Probier es selbst
#include <stdio.h>
#include "swap.h"
int main() {
char type;
scanf("%c", &type);
if (type == 'i') {
int a, b;
scanf("%d", &a);
scanf("%d", &b);
// TODO: Rufe generic_swap auf, um a und b zu tauschen
// Hinweis: Verwende &a, &b und sizeof(int)
printf("%d\n", a);
printf("%d\n", b);
} else if (type == 'd') {
double a, b;
scanf("%lf", &a);
scanf("%lf", &b);
// TODO: Rufe generic_swap auf, um a und b zu tauschen
// Hinweis: Verwende &a, &b und sizeof(double)
printf("%.2f\n", a);
printf("%.2f\n", b);
}
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der modularen Programmierung
Header-DateienInclude GuardsQuelldateienStatische FunktionenWiederholung: Modularer Taschenrechner4Kapselung
Konzept der Opaque PointersOpaque Structs definierenGetter und SetterValidierung in SetternRückblick: Die geheime Box7Funktionszeiger
Funktionszeiger deklarierenFunktionszeiger aufrufenTypedef für FunktionszeigerFunktionen als Argumente übergebenZusammenfassung: Calculator Dispatch10Generische Container
Wiederholung: Void-PointerGenerischer WrapperGenerisches SwapGenerisches VergleichenWiederholung: Generisches Array2Objekte und Methoden
Structs als ObjekteDer 'Self'-PointerConst-CorrectnessPointer vs. WertHilfsmethodenZusammenfassung: Point Manager5Projekt: Einfaches Bankkonto
Projekt-SetupImplementierung des Kontos3Objekt-Lebenszyklus
Konstruktor-MusterDestruktor-MusterStack-InitialisierungTiefe KopieRückblick: String-Wrapper6Vererbung durch Komposition
Struct-EinbettungDie First-Member-RegelZugriff auf Parent-MemberUpcastingRückblick: Formenhierarchie9Projekt: Formen-Zeichner
ProjektübersichtKreis-ImplementierungRechteck-ImplementierungPolymorphe VerwendungShape-ContainerÜbe selbstständig: Online-C-Compiler