Implement generic swap macro in C [duplicate]

2019-01-04 09:03发布

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条回答
时光不老,我们不散
2楼-- · 2019-01-04 09:10

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

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-01-04 09:14

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.

查看更多
迷人小祖宗
4楼-- · 2019-01-04 09:16

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
查看更多
女痞
5楼-- · 2019-01-04 09:22

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.

查看更多
手持菜刀,她持情操
6楼-- · 2019-01-04 09:27

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;
}
查看更多
倾城 Initia
7楼-- · 2019-01-04 09:37

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.

查看更多
登录 后发表回答