是否有内置的方法在C中交换两个变量

时间:2010-04-14 13:46:04

标签: c

我知道如何在c ++中交换2个变量,即你使用std::swap(a,b)

问题:

C标准库是否具有与C ++ std::swap()类似的功能,或者我是否必须自己定义它。

12 个答案:

答案 0 :(得分:20)

是的,您需要自己定义。

  1. C没有模板。
  2. 如果这样的功能确实存在,它看起来像void swap(void* a, void* b, size_t length),但与std::swap不同,它不是类型安全的。
  3. 并且没有暗示这样的功能可以内联,如果频繁交换(在C99中有inline关键字),这很重要。
  4. 我们也可以定义像

    这样的宏
    #define SWAP(a,b,type) {type ttttttttt=a;a=b;b=ttttttttt;}
    

    但它会隐藏ttttttttt变量,您需要重复a的类型。 (在gcc中有typeof(a)来解决此问题,但您仍然无法SWAP(ttttttttt,anything_else);。)

  5. 编写交换并不是那么困难 - 它只是3行简单的代码!

答案 1 :(得分:13)

C中没有等效物 - 事实上不可能,因为C没有模板功能。您必须为要交换的所有类型编写单独的函数。

答案 2 :(得分:9)

如果你不介意使用C语言的gcc扩展名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;
}

答案 3 :(得分:7)

这在Clang和gcc中很快起作用(但不是icc,它不能识别这个交换函数 - 但是,它会在任何标准的C99编译器中编译),只要优化实际上能识别交换(它们的确足够高)优化水平)。

#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);
}

现在解释它是如何工作的。首先,SWAP()行相对比较奇怪,但实际上相对简单。 &(a)是作为指针传递的参数a。同样,&(b)是作为指针传递的参数b

最有趣的代码是sizeof *(1 ? &(a) : &(b))。这实际上是一个相对聪明的错误报告。如果不需要错误报告,则可能只是sizeof(a)。三元运算符要求其运算具有兼容类型。在这种情况下,我通过将它们转换为指针来检查两个不同的参数,以确定它们的类型兼容性(否则,intdouble将是兼容的)。由于int *double *不兼容,编译将失败...前提是它是标准的C编译器。遗憾的是,在这种情况下,许多编译器都假设void *类型,因此它失败了,但至少有一个警告(默认情况下启用)。为了确保结果的大小正确,该值将被取消引用,并应用于sizeof,因此没有副作用。

~/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);
      ^

这个宏只评估一次(sizeof是特殊的,因为它不评估它的参数)。这为array[something()]之类的参数提供了安全性。我能想到的唯一限制是它不适用于register变量,因为它依赖于指针,但除此之外,它是通用的 - 你甚至可以将它用于可变长度数组。它甚至可以处理交换相同的变量 - 而不是你想要这样做。

答案 4 :(得分:3)

在C中,这通常是使用宏来完成的,例如:#define SWAP(type,a,b) {type _tmp=a;a=b;b=_tmp;} ...但我不建议使用他们因为有一些不明显的缺陷。

这是为避免意外错误而编写的宏。

#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)
  • 每个参数仅实例化一次,因此SWAP(a[i++], b[j++])不会产生问题副作用。
  • 临时变量名称也是SWAP,以便在不同的名称碰巧与所选的硬编码名称冲突时不会导致错误。
  • 它没有调用memcpy(实际上我在测试中最终会进行真正的函数调用,即使编译器可能会优化它们)。
  • 它的类型检查
    (作为指针进行比较会使编译器发出警告,如果它们不匹配)。

答案 5 :(得分:0)

此处未提及的另一个宏:如果您提供临时变量,则无需提供类型。另外,逗号运算符在这里用于避免do-while(0)技巧。但通常我不在乎,只是写三个命令。另一方面,如果a和b更复杂,则临时宏很有用。

#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

答案 6 :(得分:0)

检查编译器文档。编译器可能具有用于交换字节的swapb函数,并且我提供其他类似的函数。

最糟糕的情况是,浪费一天并写一些通用交换功能。它不会消耗大量项目的时间表。

答案 7 :(得分:0)

实质上,交换功能是交换两个内存块。有两个地址和块大小的字节,我们可以交换指针,整数,双精度,数组,结构,......

指针有三个部分,例如我们可以将short* p分解为三个部分

  1. 地址:void * p
  2. size:在void*p读取两个字节,我们得到一个短整数。
  3. 用法:例如使用%hu
  4. 打印一个短整数

    使用前两部分,我们将能够构建一个通用的交换函数:

    #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
    }
    

答案 8 :(得分:0)

其他答案中有很多不错的解决方案!在阅读K&R时,我想到了另一个C的快速交换宏:

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

答案 9 :(得分:0)

一些交换机制涉及-

    //( 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)

答案 10 :(得分:-2)

您可以使用宏执行类似操作,而不使用临时变量。

#include <stdio.h>

#define SWAP(a, b) {a=a+b;b=a-b;a=a-b;} //swap macro
int main(void)
{
    int a = 4, b = 5;
    float x = 4.0f, y = 5.0f;
    char *p1 = "Hello";
    char *p2 = "World";

    a = 4, b = 5,x = 4.0f, y = 5.0f,*p1 = "Hello",*p2="world";
    SWAP(a, b); // swap two ints, a and b
    SWAP(x, y); // swap two floats, x and y
    SWAP1p1, 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;
}

答案 11 :(得分:-2)

如果是数值(至少):

我知道这不是一个实际或完整的答案,但是直到现在每个人都在使用临时变量,所以我认为Chris Taylors blog可能与之相关,它确实消除了对typeof()等的需要。

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

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

理论上我认为这些技术也可以应用于字符串和其他类型。

仍然只有三个操作。