¿Hay una forma integrada de intercambiar dos variables en C

Sé cómo intercambiar 2 variables en c ++, es decir, usas std::swap(a,b) .

pregunta:

¿Tiene la biblioteca estándar de C una función similar a C ++ std::swap() o tengo que definirla yo mismo?

No hay equivalente en C, de hecho no puede haberlo, ya que C no tiene funciones de plantilla. Tendrá que escribir funciones separadas para todos los tipos que desee intercambiar.

Sí, necesitas definirlo tú mismo.

  1. C no tiene plantillas.
  2. Si tal función existe, se vería como void swap(void* a, void* b, size_t length) , pero a diferencia de std::swap , no es de tipo seguro.
  3. Y no hay indicios de que dicha función pueda estar en línea, lo cual es importante si el intercambio es frecuente (en C99 hay una palabra clave en inline ).
  4. También podríamos definir una macro como

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

    pero sombrea la variable ttttttttt , y necesita repetir el tipo de a . (En gcc hay typeof(a) para resolver esto, pero aún no puedes SWAP(ttttttttt,anything_else); )

  5. Y escribir un intercambio en su lugar tampoco es tan difícil, ¡son solo 3 líneas de código simples!

Puedes hacer algo similar con una macro si no te importa usar una extensión gcc para el lenguaje C, 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; } 

Esto funciona rápidamente en Clang y gcc (pero no en icc, que no reconoce esta función de intercambio, sin embargo, se comstackrá en cualquier comstackdor C99 estándar), siempre que las optimizaciones realmente reconozcan el intercambio (lo hacen en niveles suficientemente altos de optimización) .

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

Ahora por explicar cómo funciona. Primero, la línea SWAP() es relativamente extraña, pero en realidad es relativamente simple. &(a) es a argumento pasado como un puntero. De manera similar, &(b) es un argumento b pasado como un puntero.

El código más interesante es sizeof *(1 ? &(a) : &(b)) . Esto es en realidad una pieza de error relativamente inteligente. Si el informe de errores no fuera necesario, podría ser simplemente sizeof(a) . El operador ternario requiere que sus operaciones tengan tipos compatibles. En este caso, verifico dos argumentos diferentes para su compatibilidad de tipo al convertirlos en puntero (de lo contrario, int y double serían compatibles). Como int * y double * no son compatibles, la comstackción fallaría … siempre que sea el comstackdor C estándar. Lamentablemente, muchos comstackdores asumen el tipo void * en este caso, por lo que falla, pero al menos con una advertencia (que está habilitada de forma predeterminada). Para garantizar el tamaño correcto del resultado, el valor se elimina de referencia y se aplica a sizeof , por lo que no hay efectos secundarios.

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

Esta macro evalúa todo exactamente una vez ( sizeof es especial, ya que no evalúa sus argumentos). Esto proporciona seguridad contra argumentos como array[something()] . La única limitación que se me ocurre es que no funciona en register variables de register porque depende de los punteros, pero aparte de eso, es genérico, incluso puede usarlo para matrices de longitud variable. Incluso puede manejar el intercambio de variables idénticas, no es que quieras hacerlo.

En C esto se hace a menudo usando una macro,
Hay ejemplos muy simplistas, por ejemplo:
#define SWAP(type,a,b) {type _tmp=a;a=b;b=_tmp;}
… pero no recomendaría usarlos porque tienen algunas fallas no obvias.

Esta es una macro escrita para evitar errores accidentales.

 #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) 
  • Cada argumento es instanciado solo una vez,
    por lo que SWAP(a[i++], b[j++]) no produce efectos secundarios problemáticos.
  • El nombre de la variable temporal también es SWAP , para no causar errores si un nombre diferente choca con el nombre codificado.
  • No llama a memcpy (que de hecho terminó haciendo llamadas a funciones reales en mis pruebas, aunque un comstackdor puede optimizarlas).
  • Su tipo de verificación
    (La comparación como punteros hace que el comstackdor advierta si no coinciden).

Otra macro que no se menciona aquí: no es necesario que especifique el tipo si, en su lugar, asigna la variable temporal. Además, el operador de coma es útil aquí para evitar el truco do-while (0). Pero por lo general no me importa y simplemente escribo los tres comandos. Por otro lado, una macro temporal es útil si ayb son más complejos.

 #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 

Revise la documentación de su comstackdor. El comstackdor puede tener una función swapb para intercambiar bytes y proporcionar otras funciones similares.

En el peor de los casos, desperdicie un día y escriba algunas funciones de intercambio genéricas. No consumirá una cantidad significativa de la progtwigción de su proyecto.

en esencia, la función de intercambio es intercambiar dos bloques de memoria. Con dos direcciones y el tamaño de bloque en bytes, podemos intercambiar punteros, enteros, dobles, matrices, estructuras, …

un puntero tiene tres partes, por ejemplo, podemos dividir short* p en tres pedazos

  1. dirección: void * p
  2. tamaño: leyendo dos bytes en void*p , obtenemos un entero corto.
  3. uso: por ejemplo, imprimir un entero corto con %hu

Usando las dos primeras partes, podremos construir una función de intercambio genérica:

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

p.ej:

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

Puedes hacer algo similar con una macro sin usar una variable temporal.

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

En caso de valores numéricos (al menos):

Sé que esta no es una respuesta real o completa, pero hasta ahora todo el mundo ha estado utilizando variables temporales, por lo que pensé que el blog de Chris Taylors podría ser relevante para mencionar, sin duda elimina la necesidad de tipografía (), etc.

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

o

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

En teoría, supongo que estas técnicas podrían aplicarse también a cuerdas y otros tipos …

Aún quedan solo tres operaciones.