6
void swap(int* a, int* b) {
    if (a != b)
        *a ^= *b ^= *a ^= *b;
}

As the above *a ^= *b ^= *a ^= *b is just a shortcut for *a = *a ^ (*b = *b ^ (*a = *a ^ *b)), could (e.g.) the 2nd *a be evaluated (for the XOR) just before the 3rd *a is modified (by the =)?

Does it matter whether I write it in C99/C11/C++98/C++11?

  • I remember a discussion here about whether this is allowed in C11 with the new sequencing rule. In C99, it's clearly undefined (`*a` is modified twice without a sequence point in between). – mafso Feb 28 '15 at 13:16
  • 1
    I remember that C++ makes some additional sequencing guarantees on its assignment operators, needed because C assignment operators return values, but C++ assignment operators return lvalues, and a subsequent lvalue-to-rvalue conversion should have well-defined behaviour. The result *may* be that this is valid in C++, but I'm not sure. –  Feb 28 '15 at 13:17
  • @hvd: C11 adopted the C++ sequencing model because of threading standardization. The modification of the LHS of an assignment is now sequenced after the evaluation of the LHS and RHS. – mafso Feb 28 '15 at 13:20
  • The only thing I use the XOR hack for, is for a macro (because I don't need to know the type to declare a temporary and can use the same `SWAP` macro for all integer types. If this should expand to an expression, `#define SWAP(p, q) (*(p) ^= *(q), *(q) ^= *(p), *(p) = *(q))` is well-defined for all standards, and also has the value of updated `*p` (as the expression in the question). Is there _any_ use case for this? – mafso Feb 28 '15 at 13:25
  • 4
    @mafso; In C11, it is true that the modification of the LHS of an assignment is sequenced after the evaluation of the LHS and RHS but it doesn't guarantee that modification to RHS is sequenced before LHS, unlike is C++11. – haccks Feb 28 '15 at 13:29

1 Answers1

12

The C++11 standard says:

5.17/1: The assignment operator (=) and the compound assignment operators all group right-to-left. (...) the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

1.9/15: If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

So *a ^= *b is sequenced as follows :

  1. *a and *b are calculated. It's NOT determined in which order
  2. the xor operation is performed
  3. the assignment is done, i.e. the new value is stored in *a
  4. the new value is used as result of the expression (*a ^= *b)

Now *b ^= *a ^= *b, which according to priority rule is *b ^= (*a ^= *b) :

  1. *b and (*a ^= *b) are calculated. It's NOT determined in which order. But as *b is not modified by (*a ^= *b) it doesn't matter.
  2. the xor operation is performed
  3. the assignment is done, i.e. the new value is stored in *b

But now to the unspecified sequencing with *a ^= *b ^= *a ^= *b which is according to priority rules *a ^= (*b ^= (*a ^= *b) ):

  1. *a and (*b ^= (*a ^= *b) ) are calculated. It's NOT determined in which order. But as *a IS modified by (*b ^= (*a ^= *b) ). So the result will depend on which value is calculated first. That's clearly an U.B.

Suppose *a is evaluated first, (i.e. before anything else):
you would get the original value of it, which will be xored with the value of (*b ^= (*a ^= *b) ), that is the original *b xored with original *a xored again with *b. This will result in 0 (which will be stored in *a).

Suppose (*b ^= (*a ^= *b) ) is evaluated first, then its result is the original *a, but the content of *a is changed to the original *a xored with the original *b. Thus this will result in the original *b (which will be stored in *a)

By the way, in both cases, *b contains the original value of *a xored twice with *b meaning that *b will contain the original *a.

CONCLUSION: Here is demonstrated that the final value of *b is uniquely determined by this expression, but that the final value of *a is not uniquely defined (two values possible). So it's clearly an UNSPECIFIED/UNDEFINED RESULT ! It may swap or it might lose *a depending on your compiler.

How to make the swapping for sure ?

I've demonstrated above that first two compound assignments are well specified. So we just have to make sure that the last compound assignment is done after it. This can be guaranteed by a comma operator:

5.18/1: A pair of expressions separated by a comma is evaluated left-to-right and the value of the left expression is discarded

Hence, the following would work:

void safe_swap(int* a, int* b) {
    if (a != b)
        *b ^= *a ^= *b, *a ^= *b;
}

EDIT: But why an XOR swapping ?

On some embedded device with no more free memory, one might be obliged in extreme conditions to use such advanced trick. But it has drawbacks.

First it is difficult to understand, and as seen above, error prone. Then it might not be as efficient as it seems. Some implementation dependent experiments show less optimal code: 3 MOV and 3 XOR, compared to only 4 MOV for the classical swap using a temporary variable. Some informal benchmarks suggest that it could be slower by 3 to 8% most of the time.

By the way, the classical swap can also be written in one statement:

void modern_swap(int*a, int*b) {
    if (a!=b) 
        tie(*a,*b)=make_pair(*b,*a);
} 
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • If you were right that exactly two values are possible for `*a`, that wouldn't be undefined behaviour, that would at worst be unspecified. In most versions of the C and C++ standards, the behaviour is actually undefined, which not only means that *any* value is valid, it also means *no* value is valid (a program crash, for example). But you do actually raise an interesting point that even if the behaviour is defined, the value doesn't need to be. –  Feb 28 '15 at 15:26
  • @hvd You are right about terminology : in 5/4 its said that undefined behaviour is when "the result is not mathematically defined or not in the range of representable values for its type". Sorry if I tend to to use "undefined" in the mathematical sense. I'll correct it – Christophe Feb 28 '15 at 15:37
  • 1
    There is no such thing as "UNSPECIFIED/UNDEFINED RESULT" . Either it is undefined behaviour, or it is unspecified behaviour. – M.M Jan 23 '17 at 08:14
  • Since xor-swap is not efficient, it would be much better to actually use `{int tmp = *a; *a=*b; *b=tmp;}` as the function body. Compilers know how to use a temporary register, and can more easily inline it and optimize it away. (IMO any discussion of xor-swap needs to point out that you shouldn't use it in real life, only as a language-lawyer case that everyone's familiar with.) – Peter Cordes Sep 07 '18 at 04:05
  • 1
    @PeterCordes you are of course completely right! I just addressed the expression that OP wanted to use, without questioning the reasons that could justify this approach. With xor, it's less readable for humans, and it's 3 mov + 3 xor, against just 4 mov for the classical version. And if the goal it to use only one statement, then `tie(*a,*b)=make_pair(*b,*a);` could perfectly do the job: https://godbolt.org/z/LaaGlh - conclusion about optimisation: never do yourself what a compiler could do for you ;-) – Christophe Sep 07 '18 at 06:15
  • With a normal swap, you don't need to compare addresses. (And of course the problem with xor-swap isn't mainly just the instruction count, it's the extra latency from store/reloading multiple times). After inlining, it can be just 3 `xor reg,reg` instructions or 0 to 3 `mov reg,reg` instructions depending on context. The compiler will spill something from a reg to free up a temporary, and that's essentially always optimal. (And putting any swap function somewhere it can't inline is a bigger mistaken that using xor-swap). – Peter Cordes Sep 07 '18 at 06:40
  • Conclusion: never guide the compiler toward bad asm. They do sometimes need hand-holding to make better asm, though. e.g. my answer on [What is the efficient way to count set bits at a position or lower?](https://stackoverflow.com/q/34407437). – Peter Cordes Sep 07 '18 at 06:42
  • @PeterCordes as it is an important remark, I've edited the answer – Christophe Sep 07 '18 at 20:11
  • xor-swap is basically an assembly-language trick for swapping 2 registers without any spare registers, on machines without x86's `xchg reg,reg`. Using it in C/C++ is just silly. An embedded systems with no free memory doesn't make sense, you still have registers. Maybe an accumulator machine with only 1 register? But then doing it by reference through pointers makes no sense unless it inlines and optimizes to swapping values. You could plausibly use it to swap 2 large structs, but a normal-swap of a word at a time would be better. Maybe you can't get a compiler to emit that as easily. – Peter Cordes Sep 07 '18 at 20:32
  • 1
    Note that the conclusion that there are two possible final values for `*a` is not strictly correct according to C, nor is it the right basis for answering the question of whether the behavior is defined. That conclusion is based on the fact that a side effect on the value of `*a` (produced by assignment to `*a`) is unsequenced with respect to a value computation involving `*a` (determining the value of the left-hand operand of the leftmost `^=`). As a result of *that* the behavior is undefined. And because the behavior is undefined, the final value of `*a` could be anything at all. – John Bollinger Sep 07 '18 at 20:36
  • @JohnBollinger: Because the behavior is undefined, the question of whether to store an Unspecified value in `*a` or behave in arbitrary and capricious fashion is a Quality of Implementation issue rather than a conformance one. – supercat Sep 07 '18 at 23:20