16

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?

15
  • 19
    Of course one wouldn't actually swap like that. Use a temporary, it's cleaner and executes faster on modern hardware. Commented Sep 18, 2010 at 11:17
  • 3
    One shouldn't actually swap manualy with a temporary. in C++ exists a STL function called std::swap that does the same thing. Commented Sep 18, 2010 at 11:26
  • 17
    @Prasoon: I'm sick of these "I know Java/I know C#, trying to learn C++, in Java/C# we..." questions. The answer is stop and read a book. That's all. If you don't know beginner C++, you learn it by reading a beginner C++ book. There's no shortcuts there. Trying to guess around with "knowledge" from another language is like trying to fly a plane because you know how to ride a tricycle. That's stupid. Commented Sep 18, 2010 at 11:28
  • 6
    @Paul : No, Pattrick's problem here is different. He is not asking about Swapping two variable value without using 3rd variable. Commented Sep 18, 2010 at 11:58
  • 5
    I agree that a question about "why doesn't A work?" shouldn't be closed as a duplicate of "How to do B?" with the solution being C. Commented Sep 18, 2010 at 15:29

8 Answers 8

38

Is anything terribly wrong with the code?

Yes!

a^=b^=a^=b in fact 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.

Sign up to request clarification or add additional context in comments.

20 Comments

The modification of b is actually OK, since it is read only to determine the new value (b ^= a ^= b; would be OK).
@caf : but the order of evaluation of arguments of = operator is unspecified.
@crypto : Because this swapping method would work only for integers. :-)
@Prasoon_Saurav, The operations are simply swapping the bit representation used for a & b, how would it matter if it were a different data type?
@crypto: the ^= operator is not even defined for other types.
|
15

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 

2 Comments

To be fair, the question is tagged C as well.
@GMan: fair point! I've edited my answer accordingly. Thanks.
5

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."

6 Comments

That doesn't help. The original statement is equivalent to yours, and both have undefined behavior.
I think the edited version should work because the comma is a sequence point.
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.
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).
@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.
|
5

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)) 

4 Comments

How do you figure on the ending up 0 when a == b? Initially, a = b = X. After a ^= b, a == 0. After b ^= a, b == X. Finally, with a ^= b, a == X && b == X. That's exactly what we expect.
@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 &b = a; I edited my answer to clarify.
the solution would be to check (&(a)==&(b))
@flownt, sure you can do that, but even a & b are not pointing to the same address but have the same value, why even bother to swap?
3

Do it like this:

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

1 Comment

There is a bug in this answer, please see my answer.
1

What about this one?

a = a + b; b = a - b; a = a - b; 

Comments

0

I wondered why nobody suggested parenthesizing the expression. Seems it's not UB anymore.

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

Comments

0

You can try the following one also, but if to numbers are large enough value will overflow

a=a*b; b=a/b; a=a/b; 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.