Variablentausch mit Hilfe von Makro



  • Hallo,

    habe in meiner Lehrveranstaltung mit einer Aufgabenstellung zu kämpfen, die ich bis jetzt unmöglich lösen könnte.

    Es soll mit Hilfe eines Makros (über #define) zwei Variablen ausgetauscht werden. Soweit so gut, jedoch sit die Vorgabe, dass dies sowohl mit int, als auch float, long und double Variablen funktionieren muss. Das heißt eigentlich, dass dieses Makro ohne Hilfsvariablen funktionieren soll. Meine Frage ist dies überhaupt möglich, ohne Hilfsvariablen in Makros Variablen zu tauschen?

    Die erste Aufgabenstellung war das Tauschen zweier double Variablen mittels Funktion, welches sich aber als sehr einfach herausstellt, hier meine Funktion:

    void swap(double *a, double *b)
    {
    	double tmp = 0.;
    	tmp = *a;
    	*a = *b;
    	*b = tmp;
    }
    

    Danke für eure Hilfe im Voraus.
    Lg Christoph



  • ChrisL1988 schrieb:

    Das heißt eigentlich, dass dieses Makro ohne Hilfsvariablen funktionieren soll.

    nö, variablen gehen auch in makros, z.b. so:

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

    aufruf:

    double a = 123;
    double b = 456;
    ...
    SWAP(double,a,b);
    

    🙂



  • ;fricky schrieb:

    ChrisL1988 schrieb:

    Das heißt eigentlich, dass dieses Makro ohne Hilfsvariablen funktionieren soll.

    nö, variablen gehen auch in makros, z.b. so:

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

    aufruf:

    double a = 123;
    double b = 456;
    ...
    SWAP(double,a,b);
    

    🙂

    Hey, das klingt ja sehr gut 🙂

    Eine kleine Schönheitsfrage hätte ich noch und zwar kann ich das type irgendwie von a oder b auslesen? Weil mit dieser Schreibweise bräuchte ich ja trotzdem ein Makro für jeden Datentyp oder liege ich hier falsch?

    Danke vielmals 🙂
    Lg Christoph



  • ChrisL1988 schrieb:

    Eine kleine Schönheitsfrage hätte ich noch und zwar kann ich das type irgendwie von a oder b auslesen?

    mit nicht-standardkompatiblen spezialbefehlen geht das. der gcc z.b. hat ein 'typeof' (oder so ähnlich). ansonsten (für bis zu sizeof(long) breite variablen):

    #define SWAP(a,b) {*(unsigned long*)&a^=*(unsigned long*)&b;*(unsigned long*)&b^=*(unsigned long*)&a;*(unsigned long*)&a^=*(unsigned long*)&b;}
    

    ^^ ist aber sehr unleserlich, also mach dich schonmal darauf gefasst, dass du gefragt wirst, wie das funktioniert *fg*
    🙂



  • nachtrag: sorry, eigentlich ist das zweite makro^^ ziemlicher quatsch. es zerschiesst benachbarte variablen und kann bus-errors provozieren.
    🙂



  • 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*
    🙂


Anmelden zum Antworten