valores - intercambio de tres variables
¿Hay una forma integrada de intercambiar dos variables en C (10)
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 queSWAP(a[i++], b[j++])
no produce efectos secundarios problemáticos. - El nombre de la variable temporal también es
SWAP
, a fin de no causar errores si ocurre que 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 compilador puede optimizarlas). - Su tipo de verificación
(comparando como punteros, el compilador avisa si no coinciden).
Sé cómo intercambiar 2 variables en c ++, es decir, usas std::swap(a,b)
.
pregunta:
¿La biblioteca estándar de C
tiene una función similar a C ++ std::swap()
o tengo que definirla yo mismo?
En caso de valores numéricos (al menos):
Sé que esta no es una respuesta real o completa, pero hasta ahora todos han 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.
Esto funciona rápidamente en Clang y gcc (pero no en icc, que no reconoce esta función de intercambio, sin embargo, se compilará en cualquier compilador C99 estándar), siempre que las optimizaciones realmente reconozcan el intercambio (lo hacen en niveles suficientemente altos de optimización) .
#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);
}
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 compilación fallaría ... siempre que sea el compilador C estándar. Lamentablemente, muchos compiladores 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.
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.
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
Puedes hacer algo similar con una macro si no te importa usar una extensión gcc para el lenguaje C, 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;
}
Puedes hacer algo similar con una macro sin usar una variable temporal.
#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;
}
Revise la documentación de su compilador. El compilador 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 programación de su proyecto.
Sí, necesitas definirlo tú mismo.
- C no tiene plantillas.
- Si tal función existe, se vería como
void swap(void* a, void* b, size_t length)
, pero a diferencia destd::swap
, no es de tipo seguro. - 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
). 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 dea
. (En gcc haytypeof(a)
para resolver esto, pero aún no puedesSWAP(ttttttttt,anything_else);
)Y escribir un intercambio en su lugar tampoco es tan difícil, ¡son solo 3 líneas de código simples!
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
- dirección: void * p
- tamaño: leyendo dos bytes en
void*p
, obtenemos un entero corto. - uso: por ejemplo, imprimir un entero corto con
%hu
Usando las dos primeras partes, podremos construir una función de intercambio genérica:
#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);
}
}
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
}