Variablentausch mit Hilfe von Makro



  • Eine generische Möglichkeit:

    #include <stdlib.h>
    #include <string.h>
    
    void swap(void* left, void* right, size_t size)
    {
    	void* tmp = malloc(size);
    	memcpy(tmp, left, size);
    	memcpy(left, right, size);
    	memcpy(right, tmp, size);
    	free(tmp);
    }
    
    #define SWAP(LEFT,RIGHT) swap(&(LEFT), &(RIGHT), sizeof(LEFT))
    

    Ist allerdings nicht wahnsinnig effizient aufgrund der Speicheranforderung. Wenn man weiss, dass der grösste Typ 8 Bytes hat, kann man sowas machen:

    void swap(void* left, void* right, size_t size)
    {
    	char tmp[8];
    	memcpy(tmp, left, size);
    	memcpy(left, right, size);
    	memcpy(right, tmp, size);
    }
    

    Angewandt könnte das so aussehen:

    #include <stdio.h>
    
    int main() 
    {
    	int i1 = 4;
    	int i2 = 7;
    	double d1 = 3.14;
    	double d2 = 2.71;
    
    	SWAP(i1, i2);
    	printf("i1: %d\n", i1);
    	printf("i2: %d\n", i2);
    
    	SWAP(d1, d2);
    	printf("d1: %f\n", d1);
    	printf("d2: %f\n", d2);
    
    	SWAP(i1, i1); // ok, Wert bleibt gleich
    }
    


  • gute idee, Nexus. *daumen_hoch* als makro dann etwa so:

    #define SWAP(a,b) \
    {\
        char tmp[sizeof(a)]; \
        memcpy(&tmp, &a, sizeof(a)); \
        memcpy(&a, &b, sizeof(a)); \
        memcpy(&b, &tmp, sizeof(a)); \
    }
    

    🙂



  • Gute Ergänzung, danke. 🙂

    Wenn man auf Sicherheit steht, kann man auch Static-Assertions bezüglich Grösse einbauen (dass beide Tauschpartner gleich gross sind). Ist allerdings immer noch heikel, wenn z.B. sizeof(float) == sizeof(int) gilt.

    #define STATIC_ASSERT(EXPR) sizeof(char[EXPR]) // Kompilierfehler wenn EXPR == 0
    
    STATIC_ASSERT(sizeof(LEFT) == sizeof(RIGHT));
    

    P.S. Was ich nicht ganz sicher bin: Darf man in C auch mitten in Funktionen Variablen deklarieren? Soweit ich weiss, nur in C99, oder? Erlaubt C89 nur die Deklaration am Anfang von Funktionen, oder auch von lokalen {}-Blöcken?



  • Nexus schrieb:

    ;fricky schrieb:

    char tmp[sizeof(a)];
    

    Statische Arrays mit nicht-compiletime-konstanter Grösse gibt es doch nur in C99, oder?

    Und je nach Compilerbauergeschenk auch anderswo.
    Was ergibt sizeof(a) wenn a so ein array ist.



  • Nexus schrieb:

    Wenn man auf Sicherheit steht, kann man auch Static-Assertions bezüglich Grösse einbauen (dass beide Tauschpartner gleich gross sind). Ist allerdings immer noch heikel, wenn z.B. sizeof(float) == sizeof(int) gilt.

    #define STATIC_ASSERT(EXPR) sizeof(char[EXPR]) // Kompilierfehler wenn EXPR == 0
    STATIC_ASSERT(sizeof(LEFT) == sizeof(RIGHT));
    

    assert gibts ja schon fertig (in assert.h), das fliegt in der release-version automatisch wieder raus.

    Nexus schrieb:

    P.S. Was ich nicht ganz sicher bin: Darf man in C auch mitten in Funktionen Variablen deklarieren? Soweit ich weiss, nur in C99, oder? Erlaubt C89 nur die Deklaration am Anfang von Funktionen, oder auch von lokalen {}-Blöcken?

    ja, in c89 nur am anfang von {}-blöcken.

    volkard schrieb:

    Was ergibt sizeof(a) wenn a so ein array ist.

    na, sizeof(arrayelement)*anzahl_elemente. der memcpy-code sollte auch mit arrays gehen (wollte der OP noch nicht mal *fg*).
    🙂



  • volkard schrieb:

    Was ergibt sizeof(a) wenn a so ein array ist.

    Ich habe nachher auch gemerkt, dass das ja zur Kompilierzeit bereits bekannt ist, deshalb der Edit. Ich bin ursprünglich von meinem Beispiel mit der Funktion ausgegangen, darum habe ich gar nicht mehr daran gedacht, dass man ja im Makro direkt Zugriff auf die Grösse hat. 🙂

    ;fricky schrieb:

    assert gibts ja schon fertig (in assert.h), das fliegt in der release-version automatisch wieder raus.

    Es ging aber um ein Static-Assert, also eine Assertion zur Kompilierzeit. Jeder Fehler, der bereits während dem Kompilieren aufgedeckt werden kann, erspart mühsames Debuggen.

    ;fricky schrieb:

    ja, in c89 nur am anfang von {}-blöcken.

    Okay, danke. Ist immerhin schon besser als nur am Anfang von Funktionen... 😉



  • ;fricky schrieb:

    #define STATIC_ASSERT(EXPR) sizeof(char[EXPR]) // Kompilierfehler wenn EXPR == 0
    STATIC_ASSERT(sizeof(LEFT) == sizeof(RIGHT));
    

    assert gibts ja schon fertig (in assert.h), das fliegt in der release-version automatisch wieder raus.

    STATIC_ASSERT ist mit compilerfehler aber besser als assert. Sowas nennt sich wohl backport.

    ;fricky schrieb:

    na, sizeof(arrayelement)*anzahl_elemente

    Schön zu hören, daß es repariert wurde.



  • Nexus schrieb:

    ;fricky schrieb:

    assert gibts ja schon fertig (in assert.h), das fliegt in der release-version automatisch wieder raus.

    Es ging aber um ein Static-Assert, also eine Assertion zur Kompilierzeit. Jeder Fehler, der bereits während dem Kompilieren aufgedeckt werden kann, erspart mühsames Debuggen.

    stimmt, da ist was dran.

    volkard schrieb:

    ;fricky schrieb:

    na, sizeof(arrayelement)*anzahl_elemente

    Schön zu hören, daß es repariert wurde.

    war das nicht schon immer so? sizeof() spuckt die grösse in bytes aus, passend zu memcpy(), usw.
    🙂



  • Wie kann ich denn diesen Test mit STATIC_ASSERT oder assert einbaun?
    Ich kannte das bisher nicht, und die einfachste Lösung funktioniert nicht:

    #define STATIC_ASSERT(EXPR) sizeof(char[EXPR]) // Kompilierfehler wenn EXPR == 0
    #define SWAP(a,b) \
    {\
        STATIC_ASSERT(sizeof(a) == sizeof(b));\
        char tmp[sizeof(a)]; \
        memcpy(&tmp, &a, sizeof(a)); \
        memcpy(&a, &b, sizeof(a)); \
        memcpy(&b, &tmp, sizeof(a)); \
    }
    

    Egal was ich a,b für Sachen übergeb, da tut sich nichts, ausser dass das Ergebnis Blödsinn ist.



  • Genmutant schrieb:

    Egal was ich a,b für Sachen übergeb, da tut sich nichts, ausser dass das Ergebnis Blödsinn ist.

    Wow, da tu sich ja mehr als bei mir. Mein Compiler verweigert das grundsätzlich.



  • Big Brother schrieb:

    Genmutant schrieb:

    Egal was ich a,b für Sachen übergeb, da tut sich nichts, ausser dass das Ergebnis Blödsinn ist.

    Wow, da tu sich ja mehr als bei mir. Mein Compiler verweigert das grundsätzlich.

    hinter den zeilentrennern \ dürfen keine weiteren (unsichtbaren) zeichen in der zeile sein (tabs u.ä). viele compiler mögen das garnicht.
    🙂



  • Genmutant schrieb:

    Egal was ich a,b für Sachen übergeb, da tut sich nichts, ausser dass das Ergebnis Blödsinn ist.

    gcc erlaubt zero sized arrays. das makro hat einen compiler erwartet, der das nicht machen tut. aber gcc mag auch keine arrays mit negativen längen.

    #define STATIC_ASSERT(EXPR) {sizeof(char[(EXPR)?1:-1]);} // Kompilierfehler wenn EXPR == 0
    


  • Super, damit funktioniert es! Vielen Dank 🙂



  • ;fricky schrieb:

    hinter den zeilentrennern \ dürfen keine weiteren (unsichtbaren) zeichen in der zeile sein (tabs u.ä). viele compiler mögen das garnicht.
    🙂

    Ja, das war ein Grund. Die tmp Variable muss dann noch übers STATIC_ASSERT drüber, dann läufts.
    Aber ganz schön häßlich ist das ganze ja schon, ne.



  • volkard schrieb:

    [
    gcc erlaubt zero sized arrays. das makro hat einen compiler erwartet, der das nicht machen tut.

    gcc considered harmful? naja, aber der hat ja auch 'nen ansi-mode.

    Big Brother schrieb:

    Aber ganz schön häßlich ist das ganze ja schon, ne.

    geht so. den adressoperator vor dem 'temp' kannste weglassen, vielleicht siehts dann etwas freundlicher aus *fg*
    🙂



  • Was haltet ihr denn davon?
    #define SWAP(a, b) a=a+b; b=a-b; a=a-b;
    Sollte man vielleicht noch mit Klammern vollpumpen. Funktioniert mit double, int und float. Man kann ein Problem kriegen wenn a+b>max ist.
    #define SWAP(a, b) a>b?(a=a-b, b=a+b, a=b-a):(a=b-a, b=b-a, a=a+b)
    Irgendwie kann man immernoch einen Overflow haben wenn man a=2/3max, b=-2/3max, a-b=1.5max hat.
    Mit a<0&&b<0?(a<b?(...):(...)):(a<0&&b>=0?(...):(a>=0&&b<0?(...):(...))) sollte man das aber hinkriegen dass es immer funktioniert ohne an max oder min anzustoßen und ohne bei a>=0&&b>=0 in den negativen Bereich zu kommen wegen unsigned, ist mir jetzt aber zu kompliziert. Und überhaupt, ich will/soll ja keine Komplettlösungen liefern, du sollst ja auch deinen Spaß haben 😉



  • Bei int kann man auch XOR benutzen.

    #define SWAP(a, b) a=a^b; b=b^a; a=a^b;
    

    Bei float müsste man da bischen rummurksen, das ist dann nicht mehr schön 😉



  • volkard schrieb:

    gcc erlaubt zero sized arrays. das makro hat einen compiler erwartet, der das nicht machen tut.

    Grr, immer diese Spezialfälle... :p
    Aber gut, dass du darauf hingewiesen hast.



  • Richtig, bei float/double aber leider nicht.



  • Wo zeigt das Array denn hin, wenn es die Länge 0 hat? Auf NULL? Oder wahllos in den Speicher?


Anmelden zum Antworten