C Inhalt von 2 Variablen tauschen ohne zwischen speichern

Gast #5796246
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?
Gast #5796287
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?
Gast #5796317
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
Gast #5796358
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.
Gast #5796379
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
#5796388
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?
Gast #5796403
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.
Gast #5796493
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...
Moderator #5796502
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 :)
Gast #5796892
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
Persönliche Seite #5797005
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).
Gast #5797110
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
Persönliche Seite #5797136
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.
Gast #5797377
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.
Beitrag #5798240 wurde von einem Moderator gelöscht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren