Yes, but you have to tell the swap
code how big the elements are:
void generic_swap(void *v1, void *v2, size_t size)
{
char temp[size];
memmove(temp, v1, size);
memmove(v1, v2, size);
memmove(v2, temp, size);
}
This uses a VLA (variable length array — a feature of C99 and an optional feature of C11) for the temporary space. The size of the local array, temp
, is controlled at runtime by the function parameter size
. If you don't trust your users not to request swapping multiple megabytes of data, you can use dynamic memory allocation instead, or only use dynamic memory allocation if the size is bigger than, say, 1 kilobyte.
Either:
void generic_swap(void *v1, void *v2, size_t size)
{
size_t chunk = (size > 1024) ? 1024 : size;
size_t offset = 0;
char *s1 = v1;
char *s2 = v2;
char temp[chunk];
while (size > 0)
{
size_t length = (size > chunk) ? chunk : size;
memmove(temp, s1 + offset, length);
memmove(s1 + offset, s2 + offset, length);
memmove(s2 + offset, temp, length);
size -= length;
offset += length;
}
}
Or:
void generic_swap(void *v1, void *v2, size_t size)
{
void *v3 = malloc(size);
if (v3 != 0)
{
memmove(v3, v1, size);
memmove(v1, v2, size);
memmove(v2, v3, size);
free(v3);
}
}
The loop version avoids the overhead of dynamic memory allocation, and won't be much slower than copying it all in three operations. There are various ways that could be used to tune the looping code — see also the comments by rici about other ways in which you can optimize that if you find that the swapping code is a bottleneck.
You're at liberty to choose a smaller size than 1024 bytes; 64 or 128 might be feasible too, and you don't necessarily need a VLA in the function.
To swap two integers:
int i = 37;
int j = 99;
swap_generic(&i, &j, sizeof(i));
To swap two arrays of char
:
char data[80] = "A tabloid writer's nightmare on steroids";
char info[80] = "Obsequiousness will get you nowhere fast";
swap_generic(data, info, sizeof(data));
Etc. Note that the arrays need to be the same size — or, more accurately, the size you specify needs to be the size of the smaller of the arrays to be safe.
You can use memcpy()
instead of memmove()
if you are happy to live dangerously — though the danger is limited in this context. (If you swap an object with itself, you invoke undefined behaviour. Otherwise, it is safe.) Using memmove()
always works; using memcpy()
usually works. I prefer 'always' to 'mostly'.
Test harness for the three algorithms
Compile with, for example:
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -DUSE_GENSWAP_3 swap89.c -o swap89
When run with Valgrind, the code gets a clean bill of health.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(USE_GENSWAP_1) && !defined(USE_GENSWAP_2) && !defined(USE_GENSWAP_3)
#define USE_GENSWAP_1
#endif
extern void generic_swap(void *v1, void *v2, size_t size);
#ifdef USE_GENSWAP_1
void generic_swap(void *v1, void *v2, size_t size)
{
char temp[size];
memmove(temp, v1, size);
memmove(v1, v2, size);
memmove(v2, temp, size);
}
#endif
#ifdef USE_GENSWAP_2
void generic_swap(void *v1, void *v2, size_t size)
{
size_t chunk = (size > 1024) ? 1024 : size;
size_t offset = 0;
char *s1 = v1;
char *s2 = v2;
char temp[chunk];
while (size > 0)
{
size_t length = (size > chunk) ? chunk : size;
memmove(temp, s1 + offset, length);
memmove(s1 + offset, s2 + offset, length);
memmove(s2 + offset, temp, length);
size -= length;
offset += length;
}
}
#endif
#ifdef USE_GENSWAP_3
void generic_swap(void *v1, void *v2, size_t size)
{
void *v3 = malloc(size);
if (v3 != 0)
{
memmove(v3, v1, size);
memmove(v1, v2, size);
memmove(v2, v3, size);
free(v3);
}
}
#endif
static size_t min_len(size_t x, size_t y) { return (x < y) ? x : y; }
static void dump_long_buffer(const char *tag, size_t length, char buffer[length])
{
int maxpadlen = strlen(tag) + sizeof(" = ") - 1;
printf("%s = ", tag);
size_t offset = 0;
int padlen = 0;
while (length > 0)
{
int linelen = min_len(length, 80 - maxpadlen - sizeof("[]\n"));
printf("%*s[%.*s]\n", padlen, "", linelen, buffer + offset);
offset += linelen;
length -= linelen;
padlen = maxpadlen;
}
}
int main(void)
{
int i = 37;
int j = 99;
printf("i = %d; j = %d\n", i, j);
generic_swap(&i, &j, sizeof(i));
printf("i = %d; j = %d\n", i, j);
char data[80] = "A tabloid writer's nightmare on steroids";
char info[80] = "Obsequiousness will get you nowhere fast";
printf("data = [%s]\ninfo = [%s]\n", data, info);
generic_swap(data, info, sizeof(data));
printf("data = [%s]\ninfo = [%s]\n", data, info);
char maxibuff1[2560];
char maxibuff2[2560];
for (size_t k = 0; k < sizeof(maxibuff1); k++)
{
maxibuff1[k] = k % 64 + '!';
maxibuff2[k] = 'z' - k % 64;
}
/* The aligned output is mostly the result of serendipity */
dump_long_buffer("maxibuff1", sizeof(maxibuff1), maxibuff1);
dump_long_buffer("maxibuff2", sizeof(maxibuff2), maxibuff2);
generic_swap(maxibuff1, maxibuff2, sizeof(maxibuff1));
dump_long_buffer("maxibuff1", sizeof(maxibuff1), maxibuff1);
dump_long_buffer("maxibuff2", sizeof(maxibuff2), maxibuff2);
return 0;
}
Sample output (the result is the same from each algorithm):
i = 37; j = 99
i = 99; j = 37
data = [A tabloid writer's nightmare on steroids]
info = [Obsequiousness will get you nowhere fast]
data = [Obsequiousness will get you nowhere fast]
info = [A tabloid writer's nightmare on steroids]
maxibuff1 = [!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`]
[!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`]
…
[!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`]
maxibuff2 = [zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;]
[zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;]
…
[zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;]
maxibuff1 = [zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;]
[zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;]
…
[zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;]
maxibuff2 = [!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`]
[!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`]
…
[!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`]