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.
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.
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.
It turns out that both gcc (3.2) and Microsoft C (11.00) treat the sequence remarkably well.
printf("a=%d b=%d\n", a, b);
swap(a, b);
printf("a=%d b=%d\n", a, b);
and here is the generated code for Microsoft C.
; 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
Last modified: Monday, January 30, 2006 10:37 am
Unless otherwise expressly stated, all original material on this page created by Diomidis Spinellis is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.