Implementieren Sie ein generisches Swap-Makro in C [duplicate]
Lesezeit: 10 Minuten
Mohit Duper
Mögliches Duplikat:
Gibt es ein Äquivalent zu std::swap() in c
Hallo Leute,
Ich habe versucht, ein Problem zu schreiben, um ein generisches Swap-Makro in C zu schreiben, und mein Makro sieht so aus:
#define swap(x,y) { x = x + y; y = x - y; x = x - y; }
Es funktioniert gut für ganze Zahlen und Floats, aber ich bin mir nicht sicher, ob es einen Haken gibt. Was ist, wenn sie mit generischem Makro das Austauschen von Zeigern, Zeichen usw. meinen? Kann mir jemand beim Schreiben eines generischen Makros zum Austauschen aller Eingaben helfen?
Vielen Dank
Es wird aufgrund von Integer-Überlaufproblemen nicht funktionieren. Die Antwort von Paul R sieht so aus, wie Sie es wollen.
– nmichaels
20. Oktober 2010 um 21:21 Uhr
Dies wird nicht für alle Schwimmer funktionieren. Vorstellen float x = 1e9; float y = 1.0f;. Außerdem ist die Addition für Zeiger nicht definiert.
– Oliver Charlesworth
20. Oktober 2010 um 21:21 Uhr
Und warum machst du nicht einfach den normalen Code mit einer temporären Variable? Ich bin mir ziemlich sicher, dass der Temp-Variablencode mindestens so schnell, wenn nicht sogar schneller ist.
– CodesInChaos
20. Oktober 2010 um 21:40 Uhr
@CodeInChaos: Da es sich um ein Makro und nicht um eine tatsächliche Vorlage handelt, weiß er nicht, welchen Typ er für die temporäre Variable erstellen soll.
– Welpe
21. Oktober 2010 um 8:33 Uhr
Obwohl dies in der Tat eng mit stackoverflow.com/questions/2637856/… verwandt sein könnte, halte ich es persönlich nicht für ein Duplikat – ich habe für die Wiedereröffnung gestimmt, zumal die bisherigen Antworten und Diskussionen sehr aufschlussreich sind.
– Gregor S
12. November 2013 um 9:55 Uhr
Adam
Dies funktioniert nur mit ganzen Zahlen gut.
Für Schwimmer wird es fehlschlagen (zB versuchen Sie es mit einem sehr großen und einem sehr kleinen Schwimmer).
memcpy ist ziemlich optimiert, wenn die zu kopierende Menge zum Zeitpunkt der Kompilierung bekannt ist. Außerdem ist es nicht erforderlich, manuell einen Typnamen zu übergeben oder Compiler-spezifische Erweiterungen zu verwenden.
+1 für die einzig richtige Antwort, dann muss der Typ nicht als Argument an das Makro übergeben werden.
– R.. GitHub HÖR AUF, EIS ZU HELFEN
20. Oktober 2010 um 21:26 Uhr
Warum nicht das machen swap_temp[sizeof(x) == sizeof(y) ? sizeof(x) : -1]. Sie erhalten einen generischen Puffer und prüfen zur Kompilierzeit, ob sie gleich sind.
– GManNickG
20. Oktober 2010 um 21:26 Uhr
Dies wird stillschweigend fehlschlagen, wenn mein Variablenname lautet swap_temp.
– JaredPar
20. Oktober 2010 um 21:45 Uhr
@GMan, ja und hier ist ein echtes Szenario, in dem ich gesehen habe, dass es passiert ist (nicht speziell mit Swap, sondern mit anderen Makros, die zu Konflikten führten). Entwickler A: schreibt die Lösung mit dem von Ihnen angegebenen Namen. Entwickler B: sieht die Lösung von Entwickler A und sagt „wow, toller temporärer Name“ und verwendet sie erneut. Entwickler C verwendet das Makro von B in Kombination mit dem von A. Boom.
– JaredPar
20. Oktober 2010 um 23:07 Uhr
Die Frage war nicht “wie man ein Swap-Makro implementiert”, sondern “wie man ein Swap-Makro in C implementiert”. In C++ implementieren Sie kein Swap-Makro. So einfach ist das. Diese Frage betrifft oder bezieht sich nicht auf C++. Über C/C++ zu sprechen, insbesondere in diesem spezifischen Kontext, in dem C++ einen völlig anderen Ansatz für dasselbe Problem verfolgt, ist unglaublich falsch.
– Welpe
21. Oktober 2010 um 22:20 Uhr
Paul R
Sie können so etwas tun:
#define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0)
die Sie dann wie folgt aufrufen würden:
SWAP(a, b, int);
oder:
SWAP(x, y, float);
Wenn Sie gerne gcc-spezifische Erweiterungen verwenden, können Sie dies folgendermaßen verbessern:
#define SWAP(x, y) do { typeof(x) SWAP = x; x = y; y = SWAP; } while (0)
und dann wäre es nur:
SWAP(a, b);
oder:
SWAP(x, y);
Dies funktioniert für die meisten Typen, einschließlich Zeigern.
Hier ein Testprogramm:
#include <stdio.h>
#define SWAP(x, y) do { typeof(x) SWAP = x; x = y; y = SWAP; } while (0)
int main(void)
{
int a = 1, b = 2;
float x = 1.0f, y = 2.0f;
int *pa = &a;
int *pb = &b;
printf("BEFORE:\n");
printf("a = %d, b = %d\n", a, b);
printf("x = %f, y = %f\n", x, y);
printf("pa = %p, pb = %p\n", pa, pb);
SWAP(a, b); // swap ints
SWAP(x, y); // swap floats
SWAP(pa, pb); // swap pointers
printf("AFTER:\n");
printf("a = %d, b = %d\n", a, b);
printf("x = %f, y = %f\n", x, y);
printf("pa = %p, pb = %p\n", pa, pb);
return 0;
}
Schnell, klar, korrekt. +1
– nmichaels
20. Oktober 2010 um 21:22 Uhr
Ich bin neugierig; warum zum do{}while(0) konstruieren? Um Probleme mit der Makroerweiterung zu bekämpfen?
– Du
20. Oktober 2010 um 21:30 Uhr
@You: Es ist eine kanonische Form für Makros, die das Problem eines ungültigen anspricht ; in einem if/else-Konstrukt. Nimmst du die weg do/while (0) dann ein Ausdruck wie z if (foo) SWAP(x, y); else SWAP(a, b); wird einen Fehler erzeugen, wenn es erweitert wird if (foo) { ... }; else { ... };und die }; unmittelbar vor dem else erzeugt einen Kompilierfehler.
– PaulR
20. Oktober 2010 um 21:32 Uhr
@Paul R, Rufen Sie die Variable auf SWAPverwirrend, aber funktioniert 🙂
– Ideengeber42
22. März 2015 um 8:12 Uhr
@ideasman42: Ja, es war nicht so sehr Schatten, den dieser Kludge zu beheben versuchte – das Problem ist, dass, wenn einer der Parameter “temp” heißt und die lokale temporäre Variable auch “temp” heißt, es bricht , daher muss ein Name verwendet werden, der so eindeutig wie möglich ist. (Dies könnte natürlich sogar mit einem temporären Namen “SWAP” passieren, aber es ist höchst unwahrscheinlich, dass jemand in diesem Zusammenhang eine Variable namens “SWAP” hat.)
– PaulR
22. März 2015 um 8:20 Uhr
GMan startete diesen Versuch, dies in Kombination von an zu codieren inline Funktion und ein Makro. Diese Lösung setzt voraus, dass Sie einen modernen C-Compiler haben, der C99 unterstützt, da er a verwendet zusammengesetztes Literal:
Es hat eine Kompilierzeitprüfung für die richtigen Größen.
Es gibt kein Namensproblem mit einer versteckten Variablen.
Die Größe der temporären Variablen wird zur Kompilierzeit berechnet, daher ist das zusammengesetzte Literal kein dynamisches Array.
Die Besetzung (ptrdiff_t) wird benötigt, damit die -1 wird nicht stillschweigend befördert SIZE_MAX.
Diese Lösung leidet noch unter zwei Nachteilen:
Es ist nicht typsicher. Es überprüft nur die Größe der Typen, nicht ihre Semantik. Wenn sich die Typen unterscheiden, sagen Sie a double der Größe 8 und a uint64_tdu bist in Schwierigkeiten.
Die Ausdrücke müssen das zulassen & Betreiber anwendbar sein. Daher funktioniert es nicht mit Variablen, die mit deklariert sind register
Speicherklasse.
Ich mag, wie knapp es ist, ich wusste nicht, dass Sie das mit Arrays machen können.
– GManNickG
20. Oktober 2010 um 23:26 Uhr
sizeof *(1 ? &(a) : &(b)) wäre eine einfachere und typsicherere Option für das dritte Argument zu swap_detail . Gutschrift hier
– MM
5. November 2017 um 21:00 Uhr
Herr Lama
Einfach gesagt: du kannst kein machen generisch Swap-Makro zumindest in C, nicht ohne Risiko oder Kopfschmerzen. (Siehe andere Beiträge. Die Erklärung liegt unten.)
Makros sind nett, aber das Problem, auf das Sie mit dem tatsächlichen Code stoßen, wären Datentypprobleme (wie Sie gesagt haben). Außerdem sind Makros in gewisser Weise “dumm”. Zum Beispiel:
Mit deinem Beispielmakro #define swap(x,y) { x = x + y; y = x - y; x = x - y; }, swap(++x, y) verwandelt sich in { ++x = ++x + y; y = ++x - y; ++x = ++x - y;}.
Wenn du gerannt bist int x = 0, y = 0; swap(++x, y); du würdest bekommen x=2, y=3 Anstatt von x=0, y=0. Darüber hinaus könnten einige lästige Fehler auftreten, wenn eine der temporären Variablen in Ihrem Makro in Ihrem Code auftaucht.
Die gesuchten Funktionen wurden in C++ als Vorlagen eingeführt. Das Beste, was Sie in C erreichen können, ist die Verwendung einer Inline-Funktion für jeden vorstellbaren Datentyp oder eines anständig komplexen Makros (siehe vorherige Makroprobleme und vorherige Posts).
So sieht die Lösung mit Vorlagen in C++ aus:
template<typename T>
inline void swap(T &x, T &y)
{
T tmp = x;
x = y; y = tmp;
}
oder (wie einige Male erwähnt) ein ziemlich komplexes Makro, das gefährlich sein kann.
Dies funktioniert nicht unbedingt gut für int nach Stand. Stellen Sie sich den Fall vor, wo x und y war INT_MAX und INT_MAX-1 beziehungsweise. Die erste Additionsanweisung würde zu einem vorzeichenbehafteten Überlauf führen, der vom Standard nicht definiert ist.
Eine zuverlässigere Implementierung des Swap-Makros für int wäre der XOR-Swap-Algorithmus
-1 nur auf der Grundlage, dass der XOR-Swap-Algorithmus niemals die richtige Antwort ist …
– Oliver Charlesworth
20. Oktober 2010 um 21:20 Uhr
@Oli, das ist in der Tat ein schrecklicher Grund, abzustimmen. Es ist eine völlig gültige Antwort auf das Problem des Vertauschens von ganzen Zahlen.
– JaredPar
20. Oktober 2010 um 21:21 Uhr
@Oli: Oder ultralangsamer Assembler. Die Verwendung eines Temporärs ist auf modernen CPUs schneller.
– GManNickG
20. Oktober 2010 um 21:27 Uhr
@GMan, @Oli die in anderen Antworten bereitgestellten temporären Lösungen sind fehlerhaft. Sie alle schlagen stillschweigend fehl, wenn das Temporär den gleichen Namen hat wie der festgelegte Name im Makro.
– JaredPar
20. Oktober 2010 um 21:47 Uhr
@Paul, der neue Code vermeidet dieses Problem, funktioniert aber nur, wenn Variablen verwendet werden. Es sind keine Werte möglich, die sich aus Ausdrücken ergeben (z. B. Dereferenzierung von Zeigern). Die positive Seite ist jedoch, dass es einen Kompilierungsfehler erzwingt, anstatt still zu kompilieren und fehlzuschlagen.
– JaredPar
20. Oktober 2010 um 21:59 Uhr
Sicher
Ernsthaft, wie viele Swaps müssen Sie in Ihrem Code machen, dass es all die Kopfschmerzen wert ist, die hier in diesem Thread mit den gegebenen Lösungen auftauchen? Ich meine, dies ist keine komplizierte und fehleranfällige Codestruktur mit 10 Zeilen, sondern eine bekannte Redewendung mit einer temporären Variablen und drei einfachen Zuweisungen. Schreiben Sie es dort auf, wo Sie es brauchen, sogar in einer Zeile, wenn Sie Platz sparen möchten:
function foo ()
{
int a, b, tmp;
...
tmp = a; a = b; b = tmp;
....
}
Oder verwenden Sie ein “lokales” Makro, bei dem a und b komplexer sind.
#define SWAP(t,a,b) (
function foo (pointer)
{
int tmp;
...
SWAP(tmp, pointer->structure.array[count+1], pointer->structure.array[count+2]);
...
}
#undef SWAP
-1 nur auf der Grundlage, dass der XOR-Swap-Algorithmus niemals die richtige Antwort ist …
– Oliver Charlesworth
20. Oktober 2010 um 21:20 Uhr
@Oli, das ist in der Tat ein schrecklicher Grund, abzustimmen. Es ist eine völlig gültige Antwort auf das Problem des Vertauschens von ganzen Zahlen.
– JaredPar
20. Oktober 2010 um 21:21 Uhr
@Oli: Oder ultralangsamer Assembler. Die Verwendung eines Temporärs ist auf modernen CPUs schneller.
– GManNickG
20. Oktober 2010 um 21:27 Uhr
@GMan, @Oli die in anderen Antworten bereitgestellten temporären Lösungen sind fehlerhaft. Sie alle schlagen stillschweigend fehl, wenn das Temporär den gleichen Namen hat wie der festgelegte Name im Makro.
– JaredPar
20. Oktober 2010 um 21:47 Uhr
@Paul, der neue Code vermeidet dieses Problem, funktioniert aber nur, wenn Variablen verwendet werden. Es sind keine Werte möglich, die sich aus Ausdrücken ergeben (z. B. Dereferenzierung von Zeigern). Die positive Seite ist jedoch, dass es einen Kompilierungsfehler erzwingt, anstatt still zu kompilieren und fehlzuschlagen.
– JaredPar
20. Oktober 2010 um 21:59 Uhr
13819100cookie-checkImplementieren Sie ein generisches Swap-Makro in C [duplicate]yes
This website is using cookies to improve the user-friendliness. You agree by using the website further.
Es wird aufgrund von Integer-Überlaufproblemen nicht funktionieren. Die Antwort von Paul R sieht so aus, wie Sie es wollen.
– nmichaels
20. Oktober 2010 um 21:21 Uhr
Dies wird nicht für alle Schwimmer funktionieren. Vorstellen
float x = 1e9; float y = 1.0f;
. Außerdem ist die Addition für Zeiger nicht definiert.– Oliver Charlesworth
20. Oktober 2010 um 21:21 Uhr
Und warum machst du nicht einfach den normalen Code mit einer temporären Variable? Ich bin mir ziemlich sicher, dass der Temp-Variablencode mindestens so schnell, wenn nicht sogar schneller ist.
– CodesInChaos
20. Oktober 2010 um 21:40 Uhr
@CodeInChaos: Da es sich um ein Makro und nicht um eine tatsächliche Vorlage handelt, weiß er nicht, welchen Typ er für die temporäre Variable erstellen soll.
– Welpe
21. Oktober 2010 um 8:33 Uhr
Obwohl dies in der Tat eng mit stackoverflow.com/questions/2637856/… verwandt sein könnte, halte ich es persönlich nicht für ein Duplikat – ich habe für die Wiedereröffnung gestimmt, zumal die bisherigen Antworten und Diskussionen sehr aufschlussreich sind.
– Gregor S
12. November 2013 um 9:55 Uhr