34

Possible Duplicate:
is there an equivalent of std::swap() in c

Hi folks,

I was attempting a problem to write a generic swap macro in C and my macro looks like this:

#define swap(x,y) { x = x + y; y = x - y; x = x - y; }

It works fine for integers and floats but I am unsure if there is any catch in it. What if by generic macro they mean swapping pointers, characters etc ? Can anyone help me with writing a generic macro for swapping every input ?

Thanks

6
  • 8
    It won't work because of integer overflow issues. Paul R's answer looks like what you want. Commented Oct 20, 2010 at 21:21
  • 3
    This will not work for all floats. Imagine float x = 1e9; float y = 1.0f;. Also, addition is not defined for pointers. Commented Oct 20, 2010 at 21:21
  • 4
    And why don't you just the normal code with a temporary variable? I'm pretty sure that the temp variable code is at least as fast if not faster. Commented Oct 20, 2010 at 21:40
  • @CodeInChaos: Because, since it's a macro and not an actual template, he doesn't know what type to make the temporary variable. Commented Oct 21, 2010 at 8:33
  • 1
    While this might indeed be closely related to stackoverflow.com/questions/2637856/…, I personally don't think it qualifies as a duplicate - I've voted to reopen, especially as the answers and discussion so far are very insightful. Commented Nov 12, 2013 at 9:55

6 Answers 6

91

This works well only with integers.

For floats it will fail (e.g. try running it with a very large float and a very small one).

I would suggest something as follows:

#define swap(x,y) do \ 
   { unsigned char swap_temp[sizeof(x) == sizeof(y) ? (signed)sizeof(x) : -1]; \
     memcpy(swap_temp,&y,sizeof(x)); \
     memcpy(&y,&x,       sizeof(x)); \
     memcpy(&x,swap_temp,sizeof(x)); \
    } while(0)

memcpy is pretty optimized when the amount to copy is known at compilation time. Also, there's no need to manually pass a type name or use compiler specific extensions.

Sign up to request clarification or add additional context in comments.

32 Comments

+1 for the only correct answer than does not require passing the type as an argument to the macro.
Why not make that swap_temp[sizeof(x) == sizeof(y) ? sizeof(x) : -1]. You'll get a generic buffer and compile-time check that they're the same.
This will silently fail if my variable name is swap_temp.
@GMan, Yes and here's a real scenario where I've seen it happen (not specifically with swap but with other macros that ended up with conflicts). Developer A: writes the solution with the name you specified. Developer B: sees Developers A solution and says "wow, great temp name" and re-uses it. Developer C uses B's macro in combination with A's. Boom.
The question was not "how to implement a swap macro", it's "how to implement a swap macro in C". In C++, you don't implement a swap macro. It's that simple. This question does not concern or relate to C++. Talking about C/C++, especially in this specific context when C++ takes a totally different approach to the same problem, is incredibly wrong.
|
71

You can do something like this:

#define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0)

which you would then invoke like this:

SWAP(a, b, int);

or:

SWAP(x, y, float);

If you are happy to use gcc-specific extensions then you can improve on this like so:

#define SWAP(x, y) do { typeof(x) SWAP = x; x = y; y = SWAP; } while (0)

and then it would just be:

SWAP(a, b);

or:

SWAP(x, y);

This works for most types, including pointers.

Here is a test program:

#include <stdio.h>

#define SWAP(x, y) do { typeof(x) SWAP = x; x = y; y = SWAP; } while (0)

int main(void)
{
    int a = 1, b = 2;
    float x = 1.0f, y = 2.0f;
    int *pa = &a;
    int *pb = &b;

    printf("BEFORE:\n");
    printf("a = %d, b = %d\n", a, b);
    printf("x = %f, y = %f\n", x, y);
    printf("pa = %p, pb = %p\n", pa, pb);

    SWAP(a, b);     // swap ints
    SWAP(x, y);     // swap floats
    SWAP(pa, pb);   // swap pointers

    printf("AFTER:\n");
    printf("a = %d, b = %d\n", a, b);
    printf("x = %f, y = %f\n", x, y);
    printf("pa = %p, pb = %p\n", pa, pb);

    return 0;
}

11 Comments

Fast, clear, correct. +1
I'm curious; why the do{}while(0) construct? To combat macro expansion issues?
@You: it's a canonical form for macros which addresses the problem of an invalid ; in an if/else construct. If you take away the do/while (0) then an expression such as if (foo) SWAP(x, y); else SWAP(a, b); will generate an error, as it expands to if (foo) { ... }; else { ... };, and the }; immediately before the else will generate a compile error.
@Paul R, Call the variable SWAP, confusing but works :)
@ideasman42: yes, it wasn't so much shadowing that this kludge was trying to address though - the problem is that if one of the parameters is called say "temp" and the local temporary variable is also called "temp" then it breaks, hence the need to use a name which is as close to unique as possible. (This could even happen with a temporary named "SWAP" of course, but it's highly unlikely that anyone would have a variable named "SWAP" in this context.)
|
15

GMan started this attempt, to code this in combination of an inline function and a macro. This solution supposes that you have modern C compiler that supports C99, since it uses a compound literal:

inline void swap_detail(void* p1, void* p2, void* tmp, size_t pSize)
{
   memcpy(tmp, p1, pSize);
   memcpy(p1, p2, pSize);
   memcpy(p2 , tmp, pSize);
}
#define SWAP(a, b) swap_detail(&(a), &(b), (char[(sizeof(a) == sizeof(b)) ? (ptrdiff_t)sizeof(a) : -1]){0}, sizeof(a))

This has the following properties:

  • It evaluates each of a and b only once.
  • It has a compile time check for the correct sizes.
  • It has no naming issue with a hidden variable.
  • The size of the temporary variable is computed at compile time, so the compound literal is not a dynamic array.

The cast (ptrdiff_t) is needed such that the -1 is not silently promoted to SIZE_MAX.

This solution still suffers from two drawbacks:

  1. It is not type safe. It only checks for the sizes of the types, not their semantics. If the types differ, say a double of size 8 and a uint64_t, you are in trouble.

  2. The expressions must allow the & operator to be applicable. Thus it will not work on variables that are declared with the register storage class.

7 Comments

I like how terse it is, I didn't know you could do that with arrays.
sizeof *(1 ? &(a) : &(b)) would be a simpler and more type-safe option for the third argument to swap_detail . Credit here
@M.M Wouldn't we lose the size check with that? I am thinking of having both a compile-time size test and a compatible-type test with a helpful error message.
@Harith the compiler will reject it if a and b have incompatible types , whereas the code in the answer would accept incompatible types that happen to have the same size
@M.M I see. For VLAs, this solution fails.
|
9

Simply put: you cannot make a generic swap macro in C, at least, not without some risk or headache. (See other posts. Explanation lies below.)

Macros are nice, but the problem you'll run into with actual code would be data type issues (as you've stated). Furthermore, macros are "dumb" in a way. For example:

With your example macro #define swap(x,y) { x = x + y; y = x - y; x = x - y; }, swap(++x, y) turns into { ++x = ++x + y; y = ++x - y; ++x = ++x - y;}.

If you ran int x = 0, y = 0; swap(++x, y); you'd get x=2, y=3 instead of x=0, y=0. On top of this, if any of the temp variables in your macro appear in your code, you could run into some annoying errors.

The functionality you're looking for were introduced in C++ as templates. The closest you can get in C is using an inline function for each data type imaginable or a decently complex macro (see previous macro issues and previous posts).

Here's what that the solution using templates in C++ looks like:

template<typename T>
inline void swap(T &x, T &y)
{
    T tmp = x;
    x = y; y = tmp;
}

In C you'd need something like:

inline void swap_int(int *x, int *y) { /* Code */ }
inline void swap_char(char *x, char *y) { /* Code */ }
// etc.

or (as mentioned a few times) a fairly complex macro which has potential to be hazardous.

5 Comments

+1 for "not without some risk or headache"
No, your so-said C solution at the end isn't one, references are not part of C.
Whoops! Thanks for pointing that out. Meant for those to be pointers. Will tweak it.
The C solution is still not C. You can't have multiple definitions of a function with different parameters defined. Maybe swap_int(), swap_char(), etc.?
Man, they really didn't teach us this kind of stuff in school. Thanks for the tips guys.
0

This does not necessarily work fine for int according to the standand. Imagine the case where x and y were INT_MAX and INT_MAX-1 respectively. The first addition statement would result in a signed overflow which is undefined by the standard.

A more reliable implementation of the swap macro for int would be the XOR swap algorithm

11 Comments

-1 purely on the basis that the XOR swap algorithm is never the correct answer...
@Oli, that is in fact a terrible reason to downvote. It is a completely valid answer to the problem of swapping integers.
@Oli: Or ultra-slow assembler. Using a temporary is faster on modern CPU's.
@GMan, @Oli the temporary solutions provided in other answers are flawed. They all fail silently in the case where the temporary has the same name as the decided upon name in the macro.
@Paul, the new code avoids this problem but only works if variables are used. It's not possible values which result from expressionsn (pointer dereference for example). The plus side though is it forces a compilation error instead of silently compiling and failing.
|
-2

Seriously, how many swaps do you have to do in your code that it is worth all the headaches coming up here in this thread with the given solutions? I mean, this is not a 10 line complicated and error prone code structure, it is a well-known idiom with one temporary variable and three simple assignments. Write it where you need it, even in one line if you want to save space:

function foo ()
{
    int a, b, tmp;
    ...
    tmp = a; a = b; b = tmp;
    ....
}

Or use a "local" macro where a and b are more complex.

#define SWAP(t,a,b) ( (t) = (a), (a) = (b), (b) = (t) )

function foo (pointer)
{
    int tmp;
    ...
    SWAP(tmp, pointer->structure.array[count+1], pointer->structure.array[count+2]);
    ...
}

#undef SWAP

7 Comments

@Secure: sure just your snipsets perfectly demostrate the dangers. First of all it only assumes that the three types in question are assignment compatible. You easily will have loss of information if the types differ by accident. For your first snipset you should at least go with C99 and declare tmp there where you use it. This would at least document what type you expect. The second snipset is even worse, since it doesn't even permit the declaration at the same place. It shows how error prone this is.
@Jens: When you use an argument, then try it for both sides. If assignment compatibility is an issue at the local scope I've used here, how critical and dangerous will it become at the global scope used in the other solutions? None of them does a type compatibility check for x and y except for the sizeof, as far as I've seen, swapping pointers with ints without a warning. Your solution included.
@Secure: you are right that all these solutions suffer for types that have the same size but different semantics. But at least they check for size compatibility, yours doesn't. The problem I had in mind was a quite simple one, where e.g a is size_t and b is int. This may work for years until you encounter a buggy case. I'll add a remark for that to my solution.
@Jens: Well, these are three simple direct assignments a = b;, no complex calculations with type conversions and all that involved. Writing bugs just happens, but seriously, if you can't get this right on a regular basis, then you won't be able to write any working program beyond the complexity of Hello World, IMHO. You're talking about a general problem here, not specific to swap. How will you ever be able to guarantee the type correctness of arguments given to a function? The very same problem, but much better hidden than in the direct assignment.
@Secure: I personally find function calls much more readable. Passing on wrongly typed arguments usually clearly shows up in a debugger, e.g. But it seem that we just disagree on that point. Be it so.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.