Gibt es eine integrierte Möglichkeit, zwei Variablen in C auszutauschen?

Lesezeit: 9 Minuten

Benutzer-Avatar
Dr Deo

Ich weiß, wie man zwei Variablen in C++ austauscht, dh Sie verwenden std::swap(a,b).

Hat die C-Standardbibliothek eine ähnliche Funktion wie die von C++ std::swap()oder muss ich es selbst definieren?

  • Es gibt eine neuere Frage mit einer interessanten Antwort, die hier nicht dargestellt ist: stackoverflow.com/questions/3982348/…

    – Markieren Sie Lösegeld

    20. Oktober 2010 um 21:53 Uhr

  • Weitere Antworten (sollten wahrscheinlich zusammengeführt werden): stackoverflow.com/questions/3982348/…

    – MM

    23. März 2015 um 5:21 Uhr

Benutzer-Avatar
kennytm

Sie müssen es selbst definieren.

  1. C hat keine Vorlagen.

  2. Wenn eine solche Funktion existiert, würde sie so aussehen void swap(void* a, void* b, size_t length)aber nicht wie std::swapes ist nicht typsicher.

  3. Und es gibt keinen Hinweis darauf, dass eine solche Funktion eingebettet werden könnte, was wichtig ist, wenn häufig getauscht wird (in C99 gibt es inline Stichwort).

  4. Wir könnten auch ein Makro wie definieren

     #define SWAP(a,b,type) {type ttttttttt=a;a=b;b=ttttttttt;}
    

    aber es beschattet die ttttttttt Variable, und Sie müssen den Typ von wiederholen a. (In gcc gibt es typeof(a) um dies zu lösen, aber Sie können es immer noch nicht SWAP(ttttttttt,anything_else);.)

  5. Und das Schreiben eines Swaps an Ort und Stelle ist auch nicht so schwierig – es sind nur 3 einfache Codezeilen!

  • Um Schattenbildung zu vermeiden ttttttttt, (oder einen Fehler verursachen, wenn diese Variable als Argument übergeben wird). Sie können die Variable aufrufen SWAP zu.

    – Ideengeber42

    22. März 2015 um 16:25 Uhr


Es gibt kein Äquivalent in C – tatsächlich kann es keines geben, da C keine Template-Funktionen hat. Sie müssen separate Funktionen für alle Typen schreiben, die Sie austauschen möchten.

  • c hat also keine Standardbibliotheksalgorithmen? Austauschen ist eine häufig verwendete Funktion

    – Dr. Deo

    14. April 2010 um 14:00 Uhr

  • @Dr: C hat keine Standard-Datenstrukturbibliothek.

    – kennytm

    14. April 2010 um 14:02 Uhr


  • @Dr. Deo C hat nicht viele Dinge, die C++ tut – das ist irgendwie der Punkt.

    anon

    14. April 2010 um 15:04 Uhr

  • @Ken tatsächlich wird in C++ swap() auf alle möglichen “kreativen” Arten verwendet! Die meisten davon würden zugegebenermaßen nicht auf C-Code zutreffen.

    anon

    14. April 2010 um 15:19 Uhr

  • Da dies oft in einem Makro geschieht, ist es nicht ganz korrekt zu sagen “Sie müssen separate Funktionen für alle Typen schreiben, die Sie austauschen möchten.”

    – Ideengeber42

    22. März 2015 um 16:23 Uhr

Sie können etwas Ähnliches mit einem Makro machen, wenn es Ihnen nichts ausmacht, eine gcc-Erweiterung für die C-Sprache zu verwenden. typeof:

#include <stdio.h>

#define SWAP(a, b) do { typeof(a) temp = a; a = b; b = temp; } while (0)

int main(void)
{
    int a = 4, b = 5;
    float x = 4.0f, y = 5.0f;
    char *p1 = "Hello";
    char *p2 = "World";

    SWAP(a, b); // swap two ints, a and b
    SWAP(x, y); // swap two floats, x and y
    SWAP(p1, p2); // swap two char * pointers, p1 and p2

    printf("a = %d, b = %d\n", a, b);
    printf("x = %g, y = %g\n", x, y);
    printf("p1 = %s, p2 = %s\n", p1, p2);

    return 0;
}

  • @Neil: stimmt, aber das OP hat ausdrücklich nach a gefragt C Implementierung. Es ist bei weitem keine perfekte Lösung, aber es ist eine vernünftige Lösung für eine erhebliche Teilmenge von C-Anwendungen (IMNVHO).

    – PaulR

    14. April 2010 um 17:12 Uhr

  • Sie könnten dies mit den _generic-Ausdrücken von C11 implementieren.

    – Kasper Beyer

    6. November 2012 um 6:02 Uhr

  • @CasperBeyer Ich sehe nicht, wie das funktionieren würde struct Typen

    – MM

    23. März 2015 um 5:21 Uhr

  • Wenn man eine GCC-Erweiterung verwendet, warum nicht zwei? 🙂 Verwenden ein Aussage Ausdruck anstelle von tun, während.

    – Greg A. Woods

    8. Mai 2015 um 22:50 Uhr

  • @GregA.Woods: Ein Anweisungsausdruck ist nur nützlich, wenn Sie einen Wert zurückgeben möchten. Das do{}while()-Idiom für Makros ist gut verstanden, und jeder weiß, dass es sicher ist, selbst wenn es durch ein ersetzt wird if (foo) SWAP() else ... Kontext. Ich müsste vielleicht innehalten und darüber nachdenken, wenn es so wäre ({...; b = temp; }). Es braucht nicht viel zusätzliche mentale Zeit, um zu sehen, dass das auch in Ordnung ist, aber es ist ungleich Null, und ich sehe keinen Vorteil darin, es auf den endgültigen Wert von auszuwerten b (oder a).

    – Peter Cordes

    19. September 2016 um 23:10 Uhr

Dies funktioniert schnell in Clang und gcc (aber nicht in icc, das diese Swap-Funktion nicht erkennt – es wird jedoch in jedem Standard-C99-Compiler kompiliert), vorausgesetzt, dass die Optimierungen den Swap tatsächlich erkennen (sie tun dies auf ausreichend hohen Optimierungsstufen). .

#include <string.h>

#define SWAP(a, b) swap_internal(&(a), &(b), sizeof *(1 ? &(a) : &(b)))
static inline void swap_internal(void *a, void *b, size_t size) {
    char tmp[size];
    memcpy(tmp, a, size);
    memmove(a, b, size);
    memcpy(b, tmp, size);
}

Jetzt zur Erklärung, wie es funktioniert. Zuerst die SWAP() Linie ist relativ seltsam, aber eigentlich relativ einfach. &(a) ist Argument a als Zeiger übergeben. Ähnlich, &(b) ist Argument b als Zeiger übergeben.

Das interessanteste Stück Code ist sizeof *(1 ? &(a) : &(b)). Dies ist eigentlich eine relativ clevere Fehlerberichterstattung. Wenn keine Fehlerberichterstattung erforderlich wäre, könnte es gerecht sein sizeof(a). Der ternäre Operator erfordert, dass seine Operationen kompatible Typen haben. In diesem Fall überprüfe ich zwei verschiedene Argumente auf ihre Typkompatibilität, indem ich sie in Zeiger umwandle (andernfalls int und double kompatibel wäre). Wie int * und double * nicht kompatibel sind, würde die Kompilierung fehlschlagen … vorausgesetzt, es handelt sich um einen Standard-C-Compiler. Leider gehen viele Compiler davon aus void * Geben Sie in diesem Fall ein, so schlägt es fehl, aber zumindest mit einer Warnung (die standardmäßig aktiviert ist). Um die richtige Größe des Ergebnisses sicherzustellen, wird der Wert dereferenziert und angewendet sizeofalso keine Nebenwirkungen.

~/c/swap $ gcc swap.c
swap.c: In function ‘main’:
swap.c:5:64: warning: pointer type mismatch in conditional expression [enabled by default]
 #define SWAP(a, b) swap_internal(&(a), &(b), sizeof *(1 ? &(a) : &(b)))
                                                                ^
swap.c:16:5: note: in expansion of macro ‘SWAP’
     SWAP(cat, dog);
     ^
~/c/swap $ clang swap.c
swap.c:16:5: warning: pointer type mismatch ('int *' and 'double *') [-Wpointer-type-mismatch]
    SWAP(cat, dog);
    ^~~~~~~~~~~~~~
swap.c:5:57: note: expanded from macro 'SWAP'
#define SWAP(a, b) swap_internal(&(a), &(b), sizeof *(1 ? &(a) : &(b)))
                                                        ^ ~~~~   ~~~~
1 warning generated.
~/c/swap $ icc swap.c
swap.c(16): warning #42: operand types are incompatible ("int *" and "double *")
      SWAP(cat, dog);
      ^

Dieses Makro wertet alles genau einmal aus (sizeof ist etwas Besonderes, da es seine Argumente nicht bewertet). Das gibt Sicherheit gegen Argumente wie z array[something()]. Die einzige Einschränkung, die ich mir vorstellen kann, ist, dass es nicht funktioniert register Variablen, weil es von Zeigern abhängt, aber ansonsten ist es generisch – Sie können es sogar für Arrays mit variabler Länge verwenden. Es kann sogar mit dem Austausch identischer Variablen umgehen – nicht, dass Sie das tun möchten.

Benutzer-Avatar
Ideengeber42

In C geschieht dies oft über ein Makro,
Es gibt sehr einfache Beispiele, zB:
#define SWAP(type,a,b) {type _tmp=a;a=b;b=_tmp;}
… aber ich würde nicht empfehlen, sie zu verwenden, da sie einige nicht offensichtliche Mängel aufweisen.

Dies ist ein Makro, das geschrieben wurde, um versehentliche Fehler zu vermeiden.

#define SWAP(type, a_, b_) \
do { \
    struct { type *a; type *b; type t; } SWAP; \
    SWAP.a  = &(a_); \
    SWAP.b  = &(b_); \
    SWAP.t  = *SWAP.a; \
    *SWAP.a = *SWAP.b; \
    *SWAP.b =  SWAP.t; \
} while (0)
  • Jedes Argument wird nur einmal instanziiert,
    Also SWAP(a[i++], b[j++]) gibt keine problematischen Nebenwirkungen.
  • Temp-Variablenname ist auch SWAPum keine Fehler zu verursachen, wenn ein anderer Name zufällig mit dem fest codierten gewählten Namen kollidiert.
  • Es ruft nicht memcpy (was in meinen Tests tatsächlich zu echten Funktionsaufrufen führte, auch wenn ein Compiler sie möglicherweise optimiert).
  • Es ist typgeprüft
    (Durch den Vergleich als Zeiger warnt der Compiler, wenn sie nicht übereinstimmen).

  • Sie verwenden eine Struktur anstelle von drei separaten Variablen, da SWAP der einzige Name ist, dessen Verwendung absolut garantiert sicher ist, oder? (Sie können also nur eine Variable haben). Eine andere Möglichkeit ist die Verwendung von Dingen wie _a, _bund _tmp, da Code, der Ihr Makro verwendet, keine _prefixed-Variablen berühren sollte. Es ist in Ordnung, einen globalen Call zu beschatten _a innerhalb Ihres Makros, da keine Verwendung des Makros erforderlich sein sollte SWAP(_a, x). Aber Ihr Weg vermeidet diese Fehlerquelle sogar in schlechten Programmen, die reservierte Namen für ihre eigenen Variablen verwenden.

    – Peter Cordes

    19. September 2016 um 23:22 Uhr

  • Richtig, und einige Projekte verwenden -Wshadow Es ist also schön, dies sogar als unwahrscheinliche Möglichkeit ausschließen zu können.

    – Ideengeber42

    6. November 2017 um 2:23 Uhr


Benutzer-Avatar
Thomas Matthäus

Überprüfen Sie Ihre Compiler-Dokumentation. Der Compiler kann eine swapb Funktion zum Austauschen von Bytes und meine bieten andere ähnliche Funktionen.

Im schlimmsten Fall verschwenden Sie einen Tag und schreiben einige generische Swap-Funktionen. Es wird keinen erheblichen Teil des Zeitplans Ihres Projekts in Anspruch nehmen.

  • Sie verwenden eine Struktur anstelle von drei separaten Variablen, da SWAP der einzige Name ist, dessen Verwendung absolut garantiert sicher ist, oder? (Sie können also nur eine Variable haben). Eine andere Möglichkeit ist die Verwendung von Dingen wie _a, _bund _tmp, da Code, der Ihr Makro verwendet, keine _prefixed-Variablen berühren sollte. Es ist in Ordnung, einen globalen Call zu beschatten _a innerhalb Ihres Makros, da keine Verwendung des Makros erforderlich sein sollte SWAP(_a, x). Aber Ihr Weg vermeidet diese Fehlerquelle sogar in schlechten Programmen, die reservierte Namen für ihre eigenen Variablen verwenden.

    – Peter Cordes

    19. September 2016 um 23:22 Uhr

  • Richtig, und einige Projekte verwenden -Wshadow Es ist also schön, dies sogar als unwahrscheinliche Möglichkeit ausschließen zu können.

    – Ideengeber42

    6. November 2017 um 2:23 Uhr


Benutzer-Avatar
Sicher

Ein weiteres Makro, das hier noch nicht erwähnt wurde: Sie müssen den Typ nicht angeben, wenn Sie stattdessen die temporäre Variable angeben. Zusätzlich ist hier der Komma-Operator nützlich, um den do-while(0)-Trick zu vermeiden. Aber normalerweise ist mir das egal und ich schreibe einfach die drei Befehle. Andererseits ist ein temporäres Makro sinnvoll, wenn a und b komplexer sind.

#define SWAP(a,b,t) (

void mix_the_array (....)
{
    int tmp;
    .....
    SWAP(pointer->array[counter+17], pointer->array[counter+20], tmp);
    .....
}

#undef SWAP

1353490cookie-checkGibt es eine integrierte Möglichkeit, zwei Variablen in C auszutauschen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy