blog dds

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.

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.

  • 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);
    	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
    
  • 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.

Read and post comments, or share through   


Creative Commons License Last modified: Monday, January 30, 2006 11:37 am
Unless otherwise expressly stated, all original material on this page created by Diomidis Spinellis is licensed under a Creative Commons Attribution-Share Alike 3.0 Greece License.