Generisches Swap
Teil des Abschnitts Object Oriented Programming der C-Journey von Coddy — Lektion 51 von 61.
Eine häufige Operation in der Programmierung ist das Vertauschen zweier Werte. Normalerweise würde man für jeden Typ eine separate Swap-Funktion schreiben – eine für int, eine andere für double und so weiter. Aber mit void* und memcpy können wir eine einzige Funktion schreiben, die zwei beliebige Variablen vertauscht.
Die entscheidende Erkenntnis ist, dass das Vertauschen lediglich das Verschieben von Bytes ist. Wenn man die Größe der Daten kennt, kann man 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 size-Parameter 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
}Nun funktioniert diese eine 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 — die Verwendung von void* mit size_t — ist die Art und Weise, wie die Standardbibliothek von C generische Funktionen wie qsort und bsearch implementiert.
Aufgabe
EinfachLassen Sie uns ein generisches Swap-Utility erstellen, das die Werte von zwei beliebigen Variablen austauschen kann, unabhängig von ihrem Typ — unter Nutzung der Möglichkeiten von void*-Pointern und memcpy.
Sie werden Ihren Code über drei Dateien organisieren:
swap.h: Deklarieren Sie Ihregeneric_swap-Funktion, die zweivoid*-Pointer und einensize_t-Parameter entgegennimmt, der die Größe der zu vertauschenden Daten angibt. Vergessen Sie die Include-Guards nicht!swap.c: Implementieren Sie diegeneric_swap-Funktion. Verwenden Sie einen temporären Puffer undmemcpy, um Bytes zwischen den beiden Speicherorten zu verschieben. Denken Sie an das dreistufige Swap-Muster: Kopieren Sie den ersten Wert in den temporären Puffer, kopieren Sie den zweiten in den ersten und kopieren Sie den temporären Puffer in den zweiten.main.c: Demonstrieren Sie Ihre generische Swap-Funktion mit verschiedenen Datentypen. Lesen Sie einen Typ-Indikator (ifür Integer,dfür Doubles), gefolgt von zwei Werten. Erstellen Sie zwei Variablen des entsprechenden Typs, vertauschen Sie diese mit Ihrergeneric_swap-Funktion und geben Sie dann beide Werte nach dem Tausch aus.
Ihr Programm erhält drei Eingaben:
- Einen Typ-Indikator:
ifür Integer oderdfür Double - Den ersten Wert
- Den zweiten Wert
Geben Sie nach dem Tauschen beide Werte in separaten Zeilen aus. Verwenden Sie für Doubles 2 Dezimalstellen.
Beispielhafte Ausgabe, wenn die Eingaben i, 5 und 10 sind:
10
5Beispielhafte Ausgabe, wenn die Eingaben d, 3.14 und 2.71 sind:
2.71
3.14Beispielhafte Ausgabe, wenn die Eingaben i, -7 und 42 sind:
42
-7Beispielhafte Ausgabe, wenn die Eingaben d, 99.99 und 0.01 sind:
0.01
99.99Das Schöne an diesem Ansatz ist, dass Ihre generic_swap-Funktion keine Ahnung hat, welche Typen sie vertauscht — sie verschiebt einfach Bytes. Dieselbe Funktion funktioniert für Integer, Doubles, Structs oder jeden anderen Datentyp. Sie müssen <string.h> für memcpy und <stddef.h> oder <stdlib.h> für size_t einbinden.
Spickzettel
Eine generische Swap-Funktion verwendet void*-Pointer und memcpy, um Werte eines beliebigen Typs durch das Verschieben von Bytes zu vertauschen, ohne den tatsächlichen Typ zu kennen.
Funktionssignatur:
void generic_swap(void* a, void* b, size_t size);Implementierung unter Verwendung eines temporären Puffers und memcpy:
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
}Verwendung mit verschiedenen Typen:
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x ist jetzt 10, y ist jetzt 5
double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p ist jetzt 2.71, q ist jetzt 3.14Erforderliche Header: <string.h> für memcpy und <stddef.h> oder <stdlib.h> für size_t.
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 vertauschen
// 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 vertauschen
// 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 Object Oriented Programming
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-Implementierung