We can do it with XOR (exclusive OR) (bit operation, not a mathematical operation like add or subtract)
The definition of XOR:
0011 x
1010 y
1001 x xor y
XOR results in 1 if the arguments differ.
Now, let us pick two values a and b (we will use 4 bit values to illustrate ALL possible bit combinations, proving that the method works):
1100 a
1010 b
0110 a xor b -> a
0110 a
1010 b
1100 a xor b -> b (original a)
0110 a
1100 b
1010 a xor b -> a (original b) QED
And we have swapped our operands. Now, let us express this in C. C uses ^ for the bitwise XOR operation:
a = a ^ b;
b = a ^ b;
a = a ^ b;
We wrap this into a macro, so we can "just use it":
#define SWAP(a,b) {a = a ^ b; b = a ^ b; a = a ^ b}
Note that we normally wrap macro operands in () to avoid problems with precedence. We don't do this with SWAP, because both operands must be lvalues. So, simple values, pointer or array references will work, but:
SWAP(a+1,b)
for example, won't. We want that to produce a compilation error.
And now, we can test our new macro:
#include
#define SWAP(a,b) {a = a ^ b; b = a ^ b; a = a ^ b;}
int main(void)
{
int x = 1, y = 2;
printf("x = %d, y = %d\n", x, y);
SWAP(x,y);
printf("x = %d, y = %d"\n, x, y);
return 0;
}
This solution may be acceptable, but, in the strictest sense, even XOR could be construed as a "mathematical" operation.
--------------------
The following code works in the strict sense (that is, uses no math), but is undefined in ANSI. We don't use an additional parameter but the needed variable is "hidden" in the parameter passing.
#include
/* Most compilers will evaluate arguments from right to left. A notable
* exception is TCC. If a left to right evaluation is done, this code
* needs to be adjusted. Tested with gcc 4 and cg. Note that
* the "obvious" simplification of inlining the arg1/2 functions
* causes the code to fail in gcc.
*
* Note that this BREAKS all ANSI rules, and is, in fact "undefined"
* in terms of evaluation.
*/
int arg1(int a, int b) {return a;}
int arg2(int a, int b) {return b;}
#define SWAP(a,b) { b = arg2(a = arg2(a,b), arg1(a,b)); }
int main(void)
{
int x = 1, y = 2;
printf("x = %d, y = %d\n", x, y);
SWAP(x,y);
printf("x = %d, y = %d\n", x, y);
return 0;
}
------------------
Here is a solution that occurs to me - separate the "value" of the variable from its "location":
#include
/* A swap solution that doesn't do ANY math, and is portable.
* Uses pass by value to retain the values, and separate the values
* from their locations.
*
* It could be argued that a parameter IS an extra variable... but
* is a pointer to an existing variable? One wonders.
*
* Strictly speaking, we are not introducing new variables,
* in the sense that the new variable is holding a new
* value, or a copy of an existing variable.
*/
void swap4(int a, int b, int *pa, int *pb)
{
*pa = b;
*pb = a;
}
#define SWAP(a,b) { swap4(a,b,&a,&b); }
int main(void)
{
int x = 1, y = 2;
printf("x = %d, y = %d\n", x, y);
SWAP(x,y);
printf("x = %d, y = %d\n", x, y);
return 0;
}
This is probably the BEST solution to date - we rely on pass-by-value to separate the values and locations invoking swap4() with the separated concepts. swap4() can then legally (in the ANSI sense) accomplish the swap without the introduction of a temporary variable. WARNING: some compilers have optimization settings that include "ignore aliasing". IF that is turned on, this MAY NO LONGER WORK.