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)
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)
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;
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
and here is the generated code for Microsoft C.
printf("a=%d b=%d\n", a, b); 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.