Forum: PC-Programmierung C Inhalt von 2 Variablen tauschen ohne zwischen speichern


von Walter K. (vril1959)


Lesenswert?

Gibt es noch andere Lösungen um den Inhalt ohne Zwischenspeichern
zu tauschen?


#include<stdio.h>

int main()
{
   int x=10;   int y=25;

   x ^= y; y ^= x; x ^= y;

   printf("\n x = %i   y = %i ", x, y);
}

von Vincent H. (vinci)


Lesenswert?

Walter K. schrieb:
> Gibt es noch andere Lösungen um den Inhalt ohne Zwischenspeichern
> zu tauschen?
>
>
> #include<stdio.h>
>
> int main()
> {
>    int x=10;   int y=25;
>
>    x ^= y; y ^= x; x ^= y;
>
>    printf("\n x = %i   y = %i ", x, y);
> }
1
int x = 25; int y = 10;

von Jemand (Gast)


Lesenswert?

1
#define x y
2
#define y x

Hat sogar überhaupt keine Laufzeitkosten!

von MaWin (Gast)


Lesenswert?

Jemand schrieb:
> #define x y
> #define y x

1. April ist vorbei.

von Hanns (Gast)


Lesenswert?

In 
[[https://graphics.stanford.edu/~seander/bithacks.html#SwappingValuesSubAdd]] 
wird es mit Addition und Subtraktion gezeigt.

von Walter K. (walter_k488)


Lesenswert?

Jemand schrieb:
> #define x y
> #define y x
>
> Hat sogar überhaupt keine Laufzeitkosten!

Ah - ein Experte!

von Nop (Gast)


Lesenswert?

Hanns schrieb:
> In
> [[https://graphics.stanford.edu/~seander/bithacks.html#SwappingValuesSubAdd]]
> wird es mit Addition und Subtraktion gezeigt.

Wobei das nur für unsigned int definiert ist und bei signed int 
undefined behaviour.

von mh (Gast)


Lesenswert?

Hanns schrieb:
> In
> [[https://graphics.stanford.edu/~seander/bithacks.html#SwappingValuesSubAdd]]
> wird es mit Addition und Subtraktion gezeigt.
1
If you enable overflows exceptions, then pass unsigned values so an exception isn't thrown.
Wenn man diese Variante für signed integer benutzt möchte, sollte man 
vorher nach unsigned casten, damit man kein undefined sondern 
implementation defined behavior hat. Oder besser gleich die xor Variante 
nehmen die für signed und unsigned geeignet ist.
1
Don't use this with floating-point numbers (unless you operate on their raw integer representations).
Was ist die raw integer representation eines floats?

von Jemand (Gast)


Lesenswert?

mh schrieb:
> Was ist die raw integer representation eines floats?

Das, was du bekommst, wenn du per Union eine Gleitkommazahl als Integer 
interpretierst.

von Sebastian (Gast)


Lesenswert?

Warum?

Offensichtich geht es um C (steht in der Überschrift, und du machst dir 
um undefined behaviour Sorgen).
Du weißt also eh nicht, was nach dem Optimieren raus kommt.

Warum nimmst du nicht die am einfachsten zu verstehende Lösung, bei der 
du noch hoffen kannst dass auch der Compiler versteht was du willst, 
weil er das Muster erkennt (mit Zwischenspeichern) und läßt den Compiler 
optimieren?

von zitter_ned_aso (Gast)


Lesenswert?

Und wenn die Variablen Strings sind? Oder struct-Variablen?

von zitter_ned_aso (Gast)


Lesenswert?

Hier, mal ganz was erstaunliches ;-)
1
    int x=10;
2
    int y=20;
3
    printf("vorher: x=%d, y=%d\n", x,y);
4
5
    x=x+y;
6
    y=x-y;
7
    x=x-y;
8
9
    printf("nachher: x=%d, y=%d\n", x,y);

Ausgabe:
1
vorher: x=10, y=20
2
nachher: x=20, y=10

von zitter_ned_aso (Gast)


Lesenswert?

und mit Multiplikation/Division ;-)


1
    int x=10;
2
    int y=20;
3
    printf("vorher: x=%d, y=%d\n", x,y);
4
5
    x=x*y;
6
    y=x/y;
7
    x=x/y;
8
9
    printf("nachher: x=%d, y=%d\n", x,y);

von mh (Gast)


Lesenswert?

Jemand schrieb:
> mh schrieb:
>> Was ist die raw integer representation eines floats?
>
> Das, was du bekommst, wenn du per Union eine Gleitkommazahl als Integer
> interpretierst.
Soweit ich das sehe ist das legal, wenn man einen unsigned int 
passender Größe nimmt. Interessant.


Sebastian schrieb:
> Warum nimmst du nicht die am einfachsten zu verstehende Lösung, bei der
> du noch hoffen kannst dass auch der Compiler versteht was du willst,
> weil er das Muster erkennt (mit Zwischenspeichern) und läßt den Compiler
> optimieren?
Ich weiß nicht, was der Anlass für diese Frage ist, aber es gibt Fälle 
in denen diese Art der Optimierung notwendig ist, um Register 
einzusparen.

von mh (Gast)


Lesenswert?

zitter_ned_aso schrieb:
> Und wenn die Variablen Strings sind? Oder struct-Variablen?
2 unsigned char* + Schleife

von leo (Gast)


Lesenswert?

mh schrieb:
> aber es gibt Fälle
> in denen diese Art der Optimierung notwendig ist, um Register
> einzusparen.

Ich denke mal, das braucht mehr Register (4?) als das simple Swap (3) 
(angenommen der Item passt in ein Register).
Warum das herumgeistert ist wohl nur wegen unbekannt und erstaunlich 
Effekten.

leo

von Rolf M. (rmagnus)


Lesenswert?

mh schrieb:
> Jemand schrieb:
>> mh schrieb:
>>> Was ist die raw integer representation eines floats?
>>
>> Das, was du bekommst, wenn du per Union eine Gleitkommazahl als Integer
>> interpretierst.
> Soweit ich das sehe ist das legal, wenn man einen unsigned int
> passender Größe nimmt. Interessant.

Nein, man darf immer nur auf das Union-Element zugreifen, das "aktiv" 
ist, und das ist das zuletzt geschriebene.

leo schrieb:
> Ich denke mal, das braucht mehr Register (4?) als das simple Swap (3)

Wofür sollten so viele Register nötig sein?

von leo (Gast)


Lesenswert?

Rolf M. schrieb:
>> Ich denke mal, das braucht mehr Register (4?) als das simple Swap (3)
>
> Wofür sollten so viele Register nötig sein?

Ja, sorry - da hab ich wohl geirrt.

leo

von Jemand (Gast)


Lesenswert?

Rolf M. schrieb:
> Nein, man darf immer nur auf das Union-Element zugreifen, das "aktiv"
> ist, und das ist das zuletzt geschriebene.

C11 erlaubt das.

6.5.2.3 Structure and union members:
1
95) If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.

von mh (Gast)


Lesenswert?

Rolf M. schrieb:
> Nein, man darf immer nur auf das Union-Element zugreifen, das "aktiv"
> ist, und das ist das zuletzt geschriebene.

In C erlaubt in C++ verboten (mit Ausnahmem)

von Jobst Q. (joquis)


Lesenswert?

zitter_ned_aso schrieb:
> Und wenn die Variablen Strings sind?

Kein Problem. Pointer kann man genauso tauschen wie unsigned int.

von zitter_ned_aso (Gast)


Lesenswert?

tja, dann wollt ihr Strings in irgendwelchen Schleifen durchlaufen.

Also kommt eine Laufvariable zum Einsatz. Man hat also doch eine 
zusätzliche Variable.

von Rolf M. (rmagnus)


Lesenswert?

zitter_ned_aso schrieb:
> tja, dann wollt ihr Strings in irgendwelchen Schleifen durchlaufen.

Nö, wozu?

von zitter_ned_aso (Gast)


Lesenswert?

ja, stimmt. man kann alles per Hand machen

1
    char str1[10]="hallo";
2
    char str2[10]="welt";
3
4
    printf("vorher: %s %s\n", str1, str2);
5
6
    *str1^=*str2;
7
    *str2^=*str1;
8
    *str1^=*str2;
9
    
10
    printf("1. Durchgang: %s %s\n", str1, str2);
11
12
    *(str1+1)^=*(str2+1);
13
    *(str2+1)^=*(str1+1);
14
    *(str1+1)^=*(str2+1);
15
16
    printf("2. Durchgang: %s %s\n", str1, str2);
17
18
    //usw...

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich habe gerade etwas Interessantes festgestellt:

Der GCC erzeugt bei eingeschalteter Optimierung für alle drei Varianten
des folgenden Codes

1
#include <stdint.h>
2
3
#define METH 1
4
5
void sub2(uint8_t a, uint8_t b);
6
7
void sub1(uint8_t n, uint8_t a, uint8_t b) {
8
  for(uint8_t i=0; i<n; i++) {
9
    sub2(a, b);
10
11
#if METH==0
12
    uint8_t tmp = a;
13
    a = b;
14
    b = tmp;
15
#elif METH==1
16
    a ^= b;
17
    b ^= a;
18
    a ^= b;
19
#elif METH==2
20
    a -= b;
21
    b += a;
22
    a = b - a;
23
#endif
24
25
  }
26
}

exakt den gleichen Assemblercode. Betrachtet man diesen näher, findet
man darin die folgende Codesequenz (in Pseudoassembler, gilt sowohl für
PC als auch für AVR):

1
  move reg1 to reg3
2
  move reg2 to reg1
3
  move reg3 to reg2

Somit braucht man sich wenigstens nicht den Kopf darüber zu zerbrechen,
welche der drei Methoden die effizienteste (in Bezug auf Laufzeit oder
Speicher) ist :)

von zitter_ned_aso (Gast)


Lesenswert?

lol

von Oliver S. (oliverso)


Lesenswert?

Yalu X. schrieb:
> Somit braucht man sich wenigstens nicht den Kopf darüber zu zerbrechen,
> welche der drei Methoden die effizienteste (in Bezug auf Laufzeit oder
> Speicher) ist :)

Was dann auch zur (wenig erstaunlichen) allumfassend Antwort auf die 
Eingangsfrage führt:

Gar nicht.

Oliver

von Rolf M. (rmagnus)


Lesenswert?

zitter_ned_aso schrieb:
> ja, stimmt. man kann alles per Hand machen

Du hast es immer noch nicht verstanden: Warum soll man die Strings 
iterieren, um zwei Zeiger auszustauschen?

von Jobst Q. (joquis)


Lesenswert?

zitter_ned_aso schrieb:
> ja, stimmt. man kann alles per Hand machen
1
    char *str1 = "hallo";
2
    char *str2 = "welt";
3
4
    printf("vorher: %s %s\n", str1, str2);
5
6
    (unsigned long)str1 ^= (unsigned long)str2;
7
    (unsigned long)str2 ^= (unsigned long)str1;
8
    (unsigned long)str1 ^= (unsigned long)str2;
9
    
10
    printf("nachher: %s %s\n", str1, str2);

von x^y (Gast)


Lesenswert?

Walter K. schrieb:
> Gibt es noch andere Lösungen um den Inhalt ohne Zwischenspeichern
> zu tauschen?
>
> #include<stdio.h>
>
> int main()
> {
>    int x=10;   int y=25;
>
>    x ^= y; y ^= x; x ^= y;
>
>    printf("\n x = %i   y = %i ", x, y);
> }

Bester Tipp: Lass den Quatsch, das ist nicht effizienter. Zweitbester 
Tipp: Was passiert denn wenn x und y identisch sind (im Sinne identische 
Variable wenn du das Konstrukt später als Makro oder inline Funktion 
verpackt hast)?
1
x = y = 42;
2
 -> x ^= y;
3
 -> x = x ^ y;
4
 -> x = x ^ x;
5
 -> x = 0

von Walter K. (vril1959)


Lesenswert?

x^y schrieb:
> Was passiert denn wenn x und y identisch sind

naja .. einfach if(x^y) { /*tausche*/ }

von Thomas M. (langhaarrocker)


Lesenswert?

Mit int in C wird das so nix. Allenfalls wenn man die Nibbles eines 
Bytes vertauschen möchte könnte man beispiesweise auf ATMegas inline 
Assembler verwenden um SWAP 
(https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_SWAP.html) 
zu verwenden. Aber das ist dann nicht mehr wirklich C.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Walter K. schrieb:
> x^y schrieb:
>> Was passiert denn wenn x und y identisch sind
>
> naja .. einfach if(x^y) { /*tausche*/ }

Dann ist es aber definitiv ineffizienter als die Methode mit der
temporären Variable.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Jobst Q. schrieb:
> (unsigned long)str1 ^= (unsigned long)str2;
>     (unsigned long)str2 ^= (unsigned long)str1;
>     (unsigned long)str1 ^= (unsigned long)str2;

Und was wenn "long" nicht groß genug für einen Pointer ist? Wenn schon 
dann "uintptr_t" ...

Alternativ C++ verwenden und std::swap(x,y); aufrufen. Das nutzt 
automatisch den optimalen Weg für den jeweiligen Typ (insb. für 
Strings).

von A. S. (Gast)


Lesenswert?

zitter_ned_aso schrieb:
> Hier, mal ganz was erstaunliches ;-)

ja ;-)
Hanns schrieb:
> In
> [[https://graphics.stanford.edu/~seander/bithacks.html#SwappingValuesSubAdd]]
> wird es mit Addition und Subtraktion gezeigt.

Wobei XOR ja auch eine Addition ist, nur halt Bitweise und man nicht 
zwischen Addition und Subtraktion unterscheiden muss (daher beides mal 
XOR nehmen kann).

von [K]ein Sohn [des] Admins (frei nach Lk 3,33) (Gast)


Lesenswert?

x^y schrieb:
> Walter K. schrieb:
>> Gibt es noch andere Lösungen um den Inhalt ohne Zwischenspeichern
>> zu tauschen?
>>
>> #include<stdio.h>
>>
>> int main()
>> {
>>    int x=10;   int y=25;
>>
>>    x ^= y; y ^= x; x ^= y;
>>
>>    printf("\n x = %i   y = %i ", x, y);
>> }
>
> Bester Tipp: Lass den Quatsch, das ist nicht effizienter. Zweitbester
> Tipp: Was passiert denn wenn x und y identisch sind (im Sinne identische
> Variable wenn du das Konstrukt später als Makro oder inline Funktion
> verpackt hast)?
> x = y = 42;
>  -> x ^= y;
>  -> x = x ^ y;
>  -> x = x ^ x;
>  -> x = 0

Leider seh ich nicht was Du genau meinst. Vielleicht liegt es auch an 
der Optimierung des GCC, allerdings glaube ich das (noch) nicht.

#include<stdio.h>
1
int main()
2
{
3
   int x=25;   int y=25;
4
5
   printf("\n x = %i   y = %i ", x, y);
6
7
   x ^= y; // x=0
8
9
   printf("\n x = %i   y = %i ", x, y);
10
11
   y ^= x; // y=25
12
13
   printf("\n x = %i   y = %i ", x, y);
14
15
   x ^= y; // x=25
16
17
   printf("\n x = %i   y = %i ", x, y);
18
}
1
% ./a.out 
2
3
 x = 25   y = 25 
4
 x = 0   y = 25 
5
 x = 0   y = 25 
6
 x = 25   y = 25

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

[K]ein Sohn [des] Admins (frei nach Lk 3,33) schrieb im Beitrag 
#5797110:
> Leider seh ich nicht was Du genau meinst

Sollte man auf die Idee kommen das Tauschen (hässlich) in ein Makro zu 
packen:
1
#define UINT_SWAP(x,y) do { (x) ^= (y); (y) ^= (x); (x) ^= (y); } while (0)

und das Makro auf einer Variable mit sich selbst aufrufen (was in 
verschachtelten Konstruktionen schon mal passieren kann):
1
int main () {
2
  unsigned int a = 42;
3
  UINT_SWAP (a, a);
4
  printf ("%d\n", a);
5
}

kommt 0 heraus. Mit std::swap ist das kein Problem:
1
int main () {
2
  int a = 42;
3
  std::swap (a, a);
4
  std::cout << a << std::endl;
5
}

Das funktioniert übrigens so:
1
  /**
2
   *  @brief Swaps two values.
3
   *  @param  __a  A thing of arbitrary type.
4
   *  @param  __b  Another thing of arbitrary type.
5
   *  @return   Nothing.
6
  */
7
  template<typename _Tp>
8
    inline
9
#if __cplusplus >= 201103L
10
    typename enable_if<__and_<__not_<__is_tuple_like<_Tp>>,
11
            is_move_constructible<_Tp>,
12
            is_move_assignable<_Tp>>::value>::type
13
    swap(_Tp& __a, _Tp& __b)
14
    noexcept(__and_<is_nothrow_move_constructible<_Tp>,
15
              is_nothrow_move_assignable<_Tp>>::value)
16
#else
17
    void
18
    swap(_Tp& __a, _Tp& __b)
19
#endif
20
    {
21
      // concept requirements
22
      __glibcxx_function_requires(_SGIAssignableConcept<_Tp>)
23
24
      _Tp __tmp = _GLIBCXX_MOVE(__a);
25
      __a = _GLIBCXX_MOVE(__b);
26
      __b = _GLIBCXX_MOVE(__tmp);
27
    }

Also der klassische Ringtausch mit temporärer Variable. Wäre interessant 
ob man einen Testfall basteln kann, in dem der Optimizer den Xor-Trick 
nutzt.

: Bearbeitet durch User
von [K]ein Sohn [des] Admins (frei nach Lk 3,33) (Gast)


Lesenswert?

Niklas G. schrieb:
> [K]ein Sohn [des] Admins (frei nach Lk 3,33) schrieb im Beitrag
> #5797110:
>> Leider seh ich nicht was Du genau meinst
>
> Sollte man auf die Idee kommen das Tauschen (hässlich) in ein Makro zu
> packen:
> #define UINT_SWAP(x,y) do { (x) ^= (y); (y) ^= (x); (x) ^= (y); } while
> (0)

Danke, jetz seh ichs auch.

von Peter D. (peda)


Lesenswert?

Walter K. schrieb:
> Gibt es noch andere Lösungen um den Inhalt ohne Zwischenspeichern
> zu tauschen?

In C kümmert sich der Compiler um die Optimierung und da sollte man ihm 
gefälligst nicht ins Handwerk pfuschen.
In C gilt: Schreib es so hin, wie es am besten lesbar ist.
Irgendwelche dirty Assemblerhacks haben in C-Programmen nichts verloren.

Beitrag #5798240 wurde von einem Moderator gelöscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.