3

I typed the following program:

#include <stdio.h>

int main(void) {
    int a = 3;
    int b = 42;

    printf("a = %d\nb = %d\n", a, b);

    printf("Exchanging values.\n");
    a ^= b ^= a ^= b;

    printf("a = %d\nb = %d\n", a, b);

    return 0;
}

and it's ok. When I try to compile it, I get this:

$ gcc test.c -o test -Wall -Wextra -ansi -pedantic-errors
test.c: In function ‘main’:
test.c:11: warning: operation on ‘a’ may be undefined

That's pretty much standard code, isn't it?

Why does it trigger a warning? As far as I know, bitwise XOR is implemented by default for int as long as you are using a standard implementation of C.

Thank you very much.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
jpmelos
  • 3,283
  • 3
  • 23
  • 30
  • 8
    It should trigger a warning anyway, as it's an instance of being too clever just for the heck of it. –  Feb 27 '11 at 13:25
  • 1
    don't you mean `a ^= b; b ^= a; a ^= b;`? – Dan D. Feb 27 '11 at 13:31
  • 2
    http://en.wikipedia.org/wiki/XOR_swap_algorithm#Reasons_for_use_in_practice – Matteo Italia Feb 27 '11 at 13:34
  • @Matteo Equally: http://en.wikipedia.org/wiki/XOR_swap_algorithm#Reasons_for_avoidance_in_practice – James Greenhalgh Feb 27 '11 at 13:44
  • @James: I was actually recommending to avoid it. I linked to the section "Reasons for use in practice" because it essentially says "never". :) – Matteo Italia Feb 27 '11 at 13:46
  • I know it should not be used in real code. I'm learning. For that, I must learn everything. One must not learn only what's commercially viable, one must master everything. That's all about science. Most knowledge humankind has in math, for instance, won't ever be used in the real world, yet there's people right now researching such topics, and being actually paid to do that. Every single bit of knowlegde might come in handy eventually. Plus, read again, I never said I'd use it in a real project. You people just infer too much. – jpmelos Feb 27 '11 at 14:14
  • Ok, so you are the kind of guy who jumps off a bridge, just to see happens? :-) – Bo Persson Feb 27 '11 at 14:21
  • 1
    If I had to know what happens, I'd rather do the proper calculations and computer simulations. Math and physics are there for that. Plus, knowledge is probably useless once you are dead. – jpmelos Feb 27 '11 at 14:22
  • 2
    @jpmelos: not everybody is like you, many people after discovering this kind of tricks (XOR swap, [Duff's device](http://en.wikipedia.org/wiki/Duff%27s_device), ...) start to use them on a regular base because they seem clever or efficient; thus I think that this kind of warning is almost mandatory on questions like this. If you already knew that you should not use XOR swap for real, even better. `:)` – Matteo Italia Feb 27 '11 at 14:53
  • It should trigger an error, not a warning. This code is invalid C. – R.. GitHub STOP HELPING ICE Feb 27 '11 at 16:42
  • 1
    @jpmelos: XOR swapping may seem nice. But I find it more impressive, that all contemporary compilers recoginize the use of a temporary swap variable and optimize away its use alltogether. Either using XOR swap or some architecture depending swizzle technique. Also be warned that on contemporary architectures XOR swapping may be slower than other methods. Or for example that a few years back variables tended to be 0 initialized by XORing the register with itself. Today such "tricks" stall the codepath prediction, thus leading to worse pipeline throughput. – datenwolf Feb 27 '11 at 16:54

2 Answers2

13

Variable a is used as an lvalue twice in the expression.

Keep in mind that x ^= y is in fact a shortcut for x = x ^ y, and it means that the first operand is read, then written.

If you take the first operation out from the original expression, it is fine, see:

   b ^= a ^= b;    // OK
/*    2    1    */

Here, a is used twice and b is used three times. Since the assignment operator is right-to-left associative, first a ^= b is calculated, variable b is only read, variable a is read and then written, and the result (r1) is passed to the second operation. On the second operation, b ^= r1, b is read a second time (giving the same value as read previously) and then written. Note there is no way to interpret differently, no undefined behavior. In the above statement, a is read only once, b is read twice but both reads return the same value, and both a and b is written only once. It is ok.

When you add the third assignment to the left, it becomes a problem:

   a ^= b ^= a ^= b;    // NOT OK
/*    3    2    1    */

Now, a is read twice, once on operation 1 and once on operation 3, and also written on operation 1 and operation 3. What value should a return on operation 3, the original value or the value after operation 1 is processed?

The clever programmer may think that operation 1 is completely executed before operation 3 is processed, but this is not defined by the standard. It just happens to work with most compilers. On operation 3, the compiler may very well return the same value for a as it is returned for operation 1, causing the wrong result. This is undefined behavior.

Cole Tobin
  • 9,206
  • 15
  • 49
  • 74
Juliano
  • 39,173
  • 13
  • 67
  • 73
  • Your explanation mixes up associativity with order-of-evaluation. Saying that the operator is r-l associative only means that it is `b ^= (a ^= b)` as opposed to `(b ^= a) ^= b`. In the code `b ^= (a ^= c)`, the `b` may be read either before or after the execution of `a ^= c` – M.M Nov 26 '15 at 01:31
9

a ^= b ^= a ^= b; invokes Undefined Behaviour. You should use this:

a ^= b;
b ^= a;
a ^= b;
Community
  • 1
  • 1
Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345