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

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

题:

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

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

是的,你需要自己定义它。

  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变量,你需要重复一个类型。 (在gcc中有typeof(a)来解决这个问题,但你仍然无法SWAP(ttttttttt,anything_else);

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

如果你不介意使用C语言的gcc扩展,你可以用宏做类似的东西, typeof

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

这在Clang和gcc中很快起作用(但不是icc,它不能识别这个交换函数 – 但是,它会在任何标准C99编译器中编译),前提是优化实际上识别交换(它们在足够高的优化级别上执行) 。

 #include  #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)是作为指针传递的参数。 类似地, &(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变量,因为它依赖于指针,但除此之外,它是通用的 – 你甚至可以将它用于可变长度数组。 它甚至可以处理交换相同的变量 – 而不是你想要这样做。

在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 (实际上它最终在我的测试中进行了真正的函数调用,即使编译器可能会优化它们)。
  • 它的类型检查
    (比较指针会使编译器发出警告,如果它们不匹配)。

这里没有提到另一个宏:如果给出临时变量,则不需要给出类型。 另外,逗号运算符在这里用于避免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 

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

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

本质上,交换function是交换两个内存块。 有两个地址和块的大小(以字节为单位),我们可以交换指针,整数,双精度,数组,结构,……

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

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

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

 #include #ifdef _WIN32 #define alloca _alloca #else #include  #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); } } 

例如:

 {// 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 } 

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

 #include  #define SWAP(a, b) {a=a+b;b=ab;a=ab;} //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; } 

如果是数值(至少):

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

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

要么

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

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

仍然只有三个操作。