views:

981

answers:

5

I recently came along this method for swapping the values of two variables without using a third variable.

a^=b^=a^=b

But when I tried the above code on different compilers, I got different results, some gave correct results, some didn't.

Is anything terribly wrong with the code?

+37  A: 

Is anything terribly wrong with the code?

Yes!

a^=b^=a^=b infact invokes Undefined Behaviour in C and in C++ because you are trying to change the value of a more than once between two sequence points.


Try writing (although not foolproof )

a ^= b;
b ^= a;
a ^= b;

instead of a^=b^=a^=b.

P.S : Never try to swap the values of two variables without using a third one. Always use a third variable.

EDIT :

As @caf noticed b^=a^=b is fine even though the order of evaluation of arguments of ^= operator is unspecified, since all the accesses of b within the expression are being used to compute the final value that is being stored in b, the behaviour is well defined.

Prasoon Saurav
Would upvote but I've hit my limit ;)
Delan Azabani
The modification of `b` is actually OK, since it is read only to determine the new value (`b ^= a ^= b;` would be OK).
caf
@caf : but the order of evaluation of arguments of `=` operator is unspecified.
Prasoon Saurav
@Prasoon_Saurav,Why do you tag the 3 statements as "not foolproof"?
Kedar Soparkar
@crypto : Because this swapping method would work only for integers. :-)
Prasoon Saurav
Kedar Soparkar
@crypto: the `^=` operator is not even defined for other types.
R..
Do you mean that ^= is only defined for int types?
Kedar Soparkar
@Crypto : For `int` family. :)
Prasoon Saurav
This time it's also undefined for C++0x for the original expression. But `b ^= a ^= b` is actually fine in C++0x. The assignment to `b` is sequenced after value computation of `a ^= b`. The wording of C++03 is not clear in my opinion, but I'm not opposed to @caf's interpretation. I'm not going to bet on C++03 though :)
Johannes Schaub - litb
@Johannes : Thats for the information. I found C++-03 sequence points stuffs easier to read and understand as compared to terms like sequenced/unsequenced in C++0x. And as [R](http://stackoverflow.com/users/379897/r) said `a ^= ( a^=b , b^=a );` is also undefined in C++(and I am sure it would be well defined in C++0x). I'll miss good'ol sequence points :-)
Prasoon Saurav
@Prasoon well C++03's rules are too unclear to describe what really happens to `b ^= a ^= b` IMO. It looks innocent because the Standard says that it's fine to read and write the same variable in an expression if the read is to determine the value to write. This seemingly is the case in `b ^= a ^= b` or `i = a[i];` and similar. But there is no really strong wording in C++03 for this so I'm not going to bet, unlike for C++0x :)
Johannes Schaub - litb
@Johannes : But C++03 also says that the order of evaluation of arguments of `=` operator is unspecified. So you never know whether which side of the assignment operator gets evaluated first. :)
Prasoon Saurav
@Prasoon yes `a^= (a^=b, b^=a)` is definitely undefined in C++03 and C++0x => `a` is changed by `a^=b` (side effect) and value-computed by the left-most `a`, and both of these are not sequenced.
Johannes Schaub - litb
@Johannes : Damn! Now I am confused. You said that in `b ^= a ^= b`, the assignment to `b` is sequenced after value computation of `a ^= b` right? so that means in `a^= (a^=b, b^=a)` the assignment to left-most `a` is sequenced after the value computation of `(a^=b, b^=a)`, right? But hey, we have a comma operator in `(a^=b, b^=a)`, which would clear the side effect of `a^=b` then how would that be UB in C++0x?
Prasoon Saurav
@Prasoon it's UB for the above reason. `a = b` does not sequence the value computation of a relative to b. It doesn't matter whether `b` contains sequenced side effects of its own on `a`. They have to be sequenced relative to value computation of the left operand of `a = b` in order to be valid.
Johannes Schaub - litb
Remember we had a similar discussion [here](http://stackoverflow.com/questions/1860461/why-is-i-i-1-unspecified-behavior/1860612#1860612).
Prasoon Saurav
@Johannes : Thanks for the precise explanation BTW. :-)
Prasoon Saurav
@Johannes : And now this thread is closed. What a joke!! x-(
Prasoon Saurav
The C++0x semantics has also been discussed here: http://stackoverflow.com/questions/3690141/multiple-preincrement-operations-on-a-variable-in-cc/3691469#3691469 . Now whether it's `^=` or `+=` doesn't matter.
Johannes Schaub - litb
@Johannes : Yeah, checked your answer again. Made sense, thanks. :)
Prasoon Saurav
`b ^= a ^= b;` is defined for the same reason that `i = a[i];` is defined. It is true that `b` and `(a ^= b)` are evaluated in an unspecified order, but both must be evaluated before the new value of `b` can be calculated.
caf
@caf : I think you are right. Edited my answer accordingly.
Prasoon Saurav
+4  A: 

Do it like this:

a ^= b;
b ^= a;
a ^= b;
Matt Joiner
There is a bug in this answer, please see my answer.
grokus
+13  A: 

If you're using C++, why not use the swap algorithm in STL? It is ideal for this purpose and it's very clear what it does:

#include <algorithm>
using namespace std;

// ...

int x=5, y=10;    // x:5 y:10
swap(x,y);        // x:10 y:5
Robin Welch
To be fair, the question is tagged C as well.
GMan
@GMan: fair point! I've edited my answer accordingly. Thanks.
Robin Welch
+3  A: 

Based on contributions from R. & sellibitze:

Use the comma operator:

 (a^=b,b^=a,a^=b);

From text & Wikipedia:

"The comma operator can be used to link the related expressions together. A comma-linked list of expressions is evaluated left-to-right and the value of the rightmost expression is the value of the combined expression. It acts as a sequence point."

"A sequence point guarantees that all side effects of previous evaluations will have been performed, and no side effects from subsequent evaluations have yet been performed. It removes the undefined behavior arising out of the unclear order of execution of the original expression."

Kedar Soparkar
That doesn't help. The original statement is equivalent to yours, and both have undefined behavior.
R..
I think the edited version should work because the comma is a sequence point.
sellibitze
I think it's still undefined behavior. It's equivalent to `a = a ^ (a ^= b, b ^= a);` and the lefthand size of the `^` operator could be evaluated either before or after the parenthesized expression.
R..
The current version is defined. Still unwise though, as it is slow on modern CPUs by comparison with using a local temporary (which would of course be optimized to a register).
Donal Fellows
@R..: The version is defined, since the comma operator has its own sequence point. `a ^= b` is evaluated fully, then `b ^= a`, then `a ^= b`. Since all of those are well-defined, stringing them together with comma operators is well-defined. This doesn't apply to the similar-looking commas separating function arguments, of course.
David Thornley
@David: The answer has been edited again since my comment, which should be apparent from the content of my comment.
R..
+4  A: 

I suggest that you use std::swap() for c++.

For c, use this macro. Notice that you need to compare a and b first, otherwise when they are point to the same memory location you will wipe out the value and it becomes 0.

#define swap(a, b)  ((a) == (b) || (a) ^= (b), (b) ^= (a), (a) ^= (b))
grokus
Novelocrat
@Novelocrat, I meant a special case when a and b are the same memory location, then you would wipe out the value. E.g. int a = 5; int I edited my answer to clarify.
grokus
flownt
grokus
@grokus good point, hadn't realised that.
flownt