开发者

Is there a built-in way to swap two variables in C?

开发者 https://www.devze.com 2022-12-26 15:33 出处:网络
I know how to swap two variables in C++开发者_JS百科, ie you use std::swap(a,b). Does the C standard library have a similar function to C++\'s std::swap(), or do I have to define it myself?You need to

I know how to swap two variables in C++开发者_JS百科, ie you use std::swap(a,b).

Does the C standard library have a similar function to C++'s std::swap(), or do I have to define it myself?


You need to define it yourself.

  1. C doesn't have templates.

  2. If such function does exist it would look like void swap(void* a, void* b, size_t length), but unlike std::swap, it's not type-safe.

  3. And there's no hint such function could be inlined, which is important if swapping is frequent (in C99 there's inline keyword).

  4. We could also define a macro like

     #define SWAP(a,b,type) {type ttttttttt=a;a=b;b=ttttttttt;}
    

    but it shadows the ttttttttt variable, and you need to repeat the type of a. (In gcc there's typeof(a) to solve this, but you still cannot SWAP(ttttttttt,anything_else);.)

  5. And writing a swap in place isn't that difficult either — it's just 3 simple lines of code!


There is no equivalent in C - in fact there can't be, as C doesn't have template functions. You will have to write separate functions for all the types you want to swap.


You can do something similar with a macro if you don't mind using a gcc extension to the C language, typeof:

#include <stdio.h>

#define SWAP(a, b) do { typeof(a) temp = a; a = b; b = temp; } while (0)

int main(void)
{
    int a = 4, b = 5;
    float x = 4.0f, y = 5.0f;
    char *p1 = "Hello";
    char *p2 = "World";

    SWAP(a, b); // swap two ints, a and b
    SWAP(x, y); // swap two floats, x and y
    SWAP(p1, p2); // swap two char * pointers, p1 and p2

    printf("a = %d, b = %d\n", a, b);
    printf("x = %g, y = %g\n", x, y);
    printf("p1 = %s, p2 = %s\n", p1, p2);

    return 0;
}


This works quickly in Clang and gcc (but not icc, which doesn't recognize this swap function - however, it will compile in any standard C99 compiler), provided that the optimizations actually recognize the swap (they do on high enough optimization levels).

#include <string.h>

#define SWAP(a, b) swap_internal(&(a), &(b), sizeof *(1 ? &(a) : &(b)))
static inline void swap_internal(void *a, void *b, size_t size) {
    char tmp[size];
    memcpy(tmp, a, size);
    memmove(a, b, size);
    memcpy(b, tmp, size);
}

Now for explaining how it works. First, the SWAP() line is relatively strange, but it's actually relatively simple. &(a) is argument a passed as a pointer. Similarly, &(b) is argument b passed as an pointer.

The most interesting piece of code is sizeof *(1 ? &(a) : &(b)). This is actually a relatively clever piece of error reporting. If error reporting wouldn't be needed, it could be just sizeof(a). Ternary operator requires that its operations have compatible types. In this case, I check two different arguments for their type compatibility by converting them to pointer (otherwise, int and double would be compatible). As int * and double * aren't compatible, compilation would fail... provided it's standard C compiler. Sadly, many compilers assume void * type in this case, so it fails, but at least with a warning (that is enabled by default). To ensure correct size of the result, the value is dereferenced, and applied to sizeof, so there are no sideeffects.

~/c/swap $ gcc swap.c
swap.c: In function ‘main’:
swap.c:5:64: warning: pointer type mismatch in conditional expression [enabled by default]
 #define SWAP(a, b) swap_internal(&(a), &(b), sizeof *(1 ? &(a) : &(b)))
                                                                ^
swap.c:16:5: note: in expansion of macro ‘SWAP’
     SWAP(cat, dog);
     ^
~/c/swap $ clang swap.c
swap.c:16:5: warning: pointer type mismatch ('int *' and 'double *') [-Wpointer-type-mismatch]
    SWAP(cat, dog);
    ^~~~~~~~~~~~~~
swap.c:5:57: note: expanded from macro 'SWAP'
#define SWAP(a, b) swap_internal(&(a), &(b), sizeof *(1 ? &(a) : &(b)))
                                                        ^ ~~~~   ~~~~
1 warning generated.
~/c/swap $ icc swap.c
swap.c(16): warning #42: operand types are incompatible ("int *" and "double *")
      SWAP(cat, dog);
      ^

This macro evaluates everything exactly once (sizeof is special, as it doesn't evaluate its arguments). This provides safety against arguments like array[something()]. The only limitation I can think of is that it doesn't work on register variables because it depends on pointers, but other than that, it's generic - you can even use it for variable length arrays. It can even handle swapping identical variables - not that you would want to do that.


In C this is often done using a macro,
there are very simplistic examples, eg:
#define SWAP(type,a,b) {type _tmp=a;a=b;b=_tmp;}
... but I wouldn't recommend using them because they have some non-obvious flaws.

This is a macro written to avoid accidental errors.

#define SWAP(type, a_, b_) \
do { \
    struct { type *a; type *b; type t; } SWAP; \
    SWAP.a  = &(a_); \
    SWAP.b  = &(b_); \
    SWAP.t  = *SWAP.a; \
    *SWAP.a = *SWAP.b; \
    *SWAP.b =  SWAP.t; \
} while (0)
  • Each argument is instantiated only once,
    so SWAP(a[i++], b[j++]) doesn't give problem side effects.
  • temp variable name is also SWAP, so as not to cause bugs if a different name happens to collide with the hard-coded name chosen.
  • It doesn't call memcpy (which in fact ended up doing real function calls in my tests, even though a compiler may optimize them out).
  • Its type-checked
    (comparing as pointers makes the compiler warn if they don't match).


Check your compiler documentation. The compiler may have a swapb function for swapping bytes and my provide other similar functions.

Worst case, waste a day and write some generic swap functions. It won't consume a significant amount of your project's schedule.


Another macro not already mentioned here: You don't need to give the type if you give the temporary variable instead. Additionally the comma operator is useful here to avoid the do-while(0) trick. But usually I don't care and simply write the three commands. On the other hand a temporary macro is useful if a and b are more complex.

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

void mix_the_array (....)
{
    int tmp;
    .....
    SWAP(pointer->array[counter+17], pointer->array[counter+20], tmp);
    .....
}

#undef SWAP


in essence, swap function is to swap two memory block. with two addresses and the size of block in bytes, we can swap pointers, integers, doubles, arrays, structs, ...

a pointer has three parts, e.g. we can break down short* p into three pieces

  1. address: void* p
  2. size: reading two bytes at void*p, we get a short integer.
  3. usage: e.g. print a short integer with %hu

using the first two parts, we will be able to build a generic swap function:

#include<stdint.h> 
#ifdef _WIN32
#define alloca _alloca
#else
#include <alloca.h>
#endif

void gswap(void * const a, void * const b, int const sz) {
    // for most case, 8 bytes will be sufficient.
    int64_t tmp; // equivalent to char tmp[8];
    void * p;
    bool needfree = false;
    if (sz > sizeof(int64_t)) {
        // if sz exceed 8 bytes, we allocate memory in stack with little cost.
        p = alloca(sz);
        if (p == NULL) {
            // if sz is too large to fit in stack, we fall back to use heap.
            p = malloc(sz);
            //assert(p != NULL, "not enough memory");
            needfree = true;
        }
    }
    else {
        p = &tmp;
    }

    memcpy(p, b, sz);
    memcpy(b, a, sz);
    memcpy(a, p, sz);

    if (needfree) {
        free(p);
    }

}

e.g.:

{// swap int 
    int a = 3;
    int b = 4;
    printf("%d,%d\n", a, b);//3,4
    gswap(&a, &b, sizeof(int));
    printf("%d,%d\n", a, b);//4,3
}
{// swap int64
    int64_t a = 3;
    int64_t b = 4;
    printf("%lld,%lld\n", a, b);//3,4
    gswap(&a, &b, sizeof(int64_t));
    printf("%lld,%lld\n", a, b);//4,3
}
{// swap arrays
    int64_t a[2] = { 3,4 };
    int64_t b[2] = { 5,6 };
    printf("%lld,%lld,%lld,%lld\n", a[0], a[1], b[0], b[1]);//3,4,5,6
    gswap(&a, &b, sizeof(a));
    printf("%lld,%lld,%lld,%lld\n", a[0], a[1], b[0], b[1]);//5,6,3,4
}
{// swap arrays
    double a[2] = { 3.,4. };
    double b[2] = { 5.,6. };
    printf("%lf,%lf,%lf,%lf\n", a[0], a[1], b[0], b[1]);//3.000000, 4.000000, 5.000000, 6.000000
    arrswap(&a, &b, sizeof(a));
    printf("%lf,%lf,%lf,%lf\n", a[0], a[1], b[0], b[1]);//5.000000, 6.000000, 3.000000, 4.000000
}


one possible solution is to take a size argument, and just swap the items one byte at a time

#include <stdint.h>
#include <stdlib.h>

inline void swap_generic(void* const restrict l, void* const restrict r, const size_t size) {
    uint8_t* const left  = l;
    uint8_t* const right = r;

    for (size_t i = 0; i != size; ++i) {
        uint8_t temp_byte = right[i];
        right[i]  = left[i];
        left[i]   = temp_byte;
    }
}


Here is a solution which is arguably better than anything proposed so far:

#include <stddef.h>
#include <string.h>

inline void memswap(void * restrict buffer, void *l, void *r, size_t size) {
    memcpy(buffer, l, size);
    memmove(l, r, size);
    memcpy(r, buffer, size);
}

#define swap(a, b) \
    memswap(&(struct {\
        _Static_assert(sizeof *(a) == sizeof *(b), "arguments of swap must have same size" ); \
        char _[sizeof*(a)];\
    }) {0}, (a), (b), sizeof*(a))


void example(int *x, int *y) {
    // produces same assembly instructions as
    //     int t = *x;
    //     *x = *y
    //     *y = t;
    swap(x, y);
}

The basic idea is that we make a compound literal with the same size as *a and pass this into a memswap helper function that swaps l and r with the help of the b buffer. The compound literal also contains a _Static_assert declaration which ensures that the size of *a is equal to the size of *b.

This solution has many advantages:

  • it is safer because of the additional size test
  • the macro expands to an expression, not a statement, like all the others
  • it does not rely on VLAs, so it is more portable
  • it does not declare any local variables
  • it produces optimal code, at least for GCC and clang


Some of the swapping mechanism involve -

//( Not quite good, try passing ++x and ++y as arguments :-} )
#define SWAP_0(x,y) { x = x+y; \
                      y = x-y; \
                      x = x-y; }

//Faster than SWAP_0
#define SWAP_1(x,y) { x ^= y; \
                      y ^= x; \
                      x ^= y; }

//Optimal for general usage
#define SWAP_2(x,y)                                                                         \
                 do                                                                         \
                {                                                                           \
                    uint8_t __temp[sizeof(x) == sizeof(y) ? (signed)sizeof(x) : -1];        \
                    memcpy(__temp,     &y, sizeof(x));                                      \
                    memcpy(    &y,     &x, sizeof(x));                                      \
                    memcpy(    &x, __temp, sizeof(x));                                      \
                } while(0)

//using GCC specific extension
#define SWAP_3(x, y) do                                                                     \
                    {                                                                       \
                        typeof(x) SWAP_3 = x; x = y; y = SWAP_3;                            \
                    }while (0)

//without GCC specific extension - can be invoked like this - SWAP_3(x,y, int) or SWAP_3(x,y, float)
#define SWAP_4(x, y, T) do                                                                  \
                    {                                                                       \
                        T SWAP_4 = x; x = y; y = SWAP_4;                                    \
                    }while (0)


Following macro does this in a type-safe way and works with different types:

#include <memory.h>

#define SWAP(a, b) do { char tmp1[sizeof(a)], tmp2[sizeof(a)]; \
    memcpy(tmp1, &(a), sizeof(a)); \
    (a) = (b); \
    memcpy(tmp2, &(a), sizeof(a)); \
    memcpy(&(a), tmp1, sizeof(a)); \
    (b) = (a); \
    memcpy(&(a), tmp2, sizeof(a)); \
    } while(0)

This macro

  1. first stores original value of a in the temp buffer tmp1
  2. gets new value of a by assignment from b
  3. stores the new value of a to the temp buffer tmp2
  4. restores original value of a from tmp1
  5. gets final value of b by assignment from a
  6. finally restores the correct new value of a from tmp2

The Godbolt.org compiler explorer demonstrates that at least GCC is able to optimize this nicely already at -O2.


Lots of nice solutions in the other answers! Just another quick swap macro for C that I came up with when reading K&R:

#define SWAP(TYPE,x,y) TYPE a = x, x=y, y=a


In case of numeric values (at least):

I know this is not an actual or complete answer but until now everyone has been making use of temporary variables so I thought Chris Taylors blog might be relevant to mention, it certainly does remove the need for typeof() etc.

a = a ^ b;
b = a ^ b;
a = a ^ b;

or

a = a + b;
b = a - b;
a = a - b;

In theory I suppose these techniques could be applied to strings and other types as well..

Still only three operations.

0

精彩评论

暂无评论...
验证码 换一张
取 消