1

I tried to swap without a template and I encountered this. Are a+=k and a=a+b different? What is wrong for first case?

a += b-(b=a); // this print same value of two a and b.

a = a + b-(b=a); // this thing correctly swapped values.  
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
bilmemne
  • 29
  • 2

3 Answers3

4

Both result in undefined behavior because you're both accessing and modifying b without an intervening sequence point:

6.5p2:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.

Here's how you could do it with +/- (UB-free only for unsigned numeric types as signed +/- may result in undefined overflow):

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

or with xor (always UB-free):

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

Gcc and clang appear to recognize both of these patterns and compile them to a swap via a temporary, which is the most efficient method on common architectures: https://gcc.godbolt.org/z/3W_22r

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
2

Undefined Behavior in the Posted Code

Both statements lead to undefined behavior, so there should be no expectation that they give the same result.

From the C11 Draft Standard §6.5p2

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

In the posted code, (b=a) is an expression that evaluates to the value of a, but has the side effect of assigning the value of a to b. In both statements, the value of (b=a) (which is the value of a) is subtracted from b, but here b is a value computation resulting in the value of b. There is no sequence point between these two expressions, which is to say that whether the value computation b is sequenced before or after (b=a) is indeterminate. Since the side effect on b and the value computation of b are unsequenced in both statements, both lead to undefined behavior.

Why Do You Need to Write Code Like This, Anyway?

It is best to avoid "clever" code such as this. In this case the seemingly clever code has undefined behavior; in other cases the clever code may just be difficult to understand for others who must maintain the code, or even for yourself at a later date. Write clear code that is easy to understand and easy to maintain. Only worry about optimizations when you encounter performance issues and have identified the culprit. Compilers are smart, they recognize common idioms, and are able to produce near optimal code for those common idioms when optimizations are turned on. Rarely would you need comma operators or unintuitive XOR swaps; for a simple swap, which is such a common operation in programming, prefer the obvious solution and let your compiler do its job.

The obvious solution, using a temporary variable of the same type as a and b, is easy to understand, can never have undefined behavior (or even implementation-dependent behavior), and will often be optimized by a good compiler to code that is more performant than code coming from well-meaning programmer micro-optimizations:

temp = a;
a = b;
b = temp;
ad absurdum
  • 19,498
  • 5
  • 37
  • 60
0

From C11 standard 6.5.16.2.3

A compound assignment of the form E1 op= E2 is equivalent to the simple assignment expression E1 = E1 op (E2), except that the lvalue E1 is evaluated only once, and with respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.

Tarek Dakhran
  • 2,021
  • 11
  • 21