2006.01.30
A General-Purpose Swap Macro
A couple of days ago I came up with a general-purpose macro for swapping values in C programs. My colleague Panagiotis Louridas suggested an improvement, and this prompted me to see the two macros got compiled.
Here is the original macro:
/* General-purpose swap macro */
#define swap2(a, b) do {\
struct s_swap { char c[sizeof(a)]; } swap_tmp; \
assert(sizeof(swap_tmp) == sizeof(a)); \
swap_tmp = *(struct s_swap *)&a; \
*(struct s_swap *)&a = *(struct s_swap *)&b; \
*(struct s_swap *)&b = swap_tmp; \
} while (0)
The macro works by aliasing the two elements to be copied and
the intermediate temporary value with a structure ofan equal size.
Structures can be copied as first-class citizens in C, and therefore
the macro works with any element you care to pass to it: int, double, char,
even arrays.
It is slightly dangerous, because there is no guarantee that the size of
the aliased structure will be exactly the same as that of the values.
For this reason I've used an assertion, and it therefore
requires including the assert.h header.
#define swap2(a, b) do {\
struct s_swap { char c[sizeof(a)]; } swap_tmp; \
assert(sizeof(swap_tmp) == sizeof(a)); \
swap_tmp = *(struct s_swap *)&a; \
*(struct s_swap *)&a = *(struct s_swap *)&b; \
*(struct s_swap *)&b = swap_tmp; \
} while (0)
Here is the improved macro (Panagiotis used a = b in the second step, but that wouldn't work for arrays):
/* General-purpose swap macro */
#define swap(a, b) do {\
char c[sizeof(a)]; \
memcpy((void *)&c, (void *)&a, sizeof(c)); \
memcpy((void *)&a, (void *)&b, sizeof(a)); \
memcpy((void *)&b, (void *)&c, sizeof(b)); \
} while (0)
This version of the macro is more readable and portable.
It requires including the string.h header.
#define swap(a, b) do {\
char c[sizeof(a)]; \
memcpy((void *)&c, (void *)&a, sizeof(c)); \
memcpy((void *)&a, (void *)&b, sizeof(a)); \
memcpy((void *)&b, (void *)&c, sizeof(b)); \
} while (0)
So, how do the macros compare with the standard code, such as the example below?
int a, b, tmp;
tmp = a;
a = b;
b = tmp;
The last thing that we'd want would be that the three assignments of
the above sequence be turned into three calls to memcpy.
tmp = a;
a = b;
b = tmp;
It turns out that both gcc (3.2) and Microsoft C (11.00) treat the sequence remarkably well.
- For integers, both compilers with both versions of the macro
will recognize the swap, and simply use a for b and b for a!
Here is the C code
printf("a=%d b=%d\n", a, b);and here is the generated code for Microsoft C.
swap(a, b);
printf("a=%d b=%d\n", a, b);
; Line 16
push esi
push ebx
push OFFSET FLAT:$SG196
call _printf
add esp, 12 ; 0000000cH
; Line 18
push ebx
push esi
push OFFSET FLAT:$SG201
call _printf
- For values of type double and the structure-based copy, the situation is the same; with the memcpy version gcc will actually swap the values by using registers. Again, not bad.
- I've not exhaustively tested many other possible cases (for example, different basic types, arrays, and volatile values), but some other examples I tried show that the macro behaves no worse than what you would achieve by hand-coding it.

