Implementar macro de intercambio genérico en C

Posible duplicado:
hay un equivalente de std :: swap () en c

Hola amigos,

Estaba intentando un problema para escribir una macro de intercambio genérico en C y mi macro se ve así:

#define swap(x,y) { x = x + y; y = x - y; x = x - y; } 

Funciona bien para enteros y flotadores, pero no estoy seguro si hay alguna trampa en él. ¿Qué pasa si por macro genérico significan intercambiar punteros, caracteres, etc.? ¿Alguien puede ayudarme a escribir una macro genérica para intercambiar cada entrada?

Gracias

Esto funciona bien solo con enteros.

Para los flotadores fallará (por ejemplo, intente ejecutarlo con un flotador muy grande y uno muy pequeño).

Sugeriría algo de la siguiente manera:

 #define swap(x,y) do \ { unsigned char swap_temp[sizeof(x) == sizeof(y) ? (signed)sizeof(x) : -1]; \ memcpy(swap_temp,&y,sizeof(x)); \ memcpy(&y,&x, sizeof(x)); \ memcpy(&x,swap_temp,sizeof(x)); \ } while(0) 

memcpy está bastante optimizado cuando se conoce la cantidad a copiar en el momento de la comstackción. Además, no es necesario pasar manualmente un nombre de tipo o usar extensiones específicas del comstackdor.

Puedes hacer algo como esto:

 #define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0) 

que invocarías así:

 SWAP(a, b, int); 

o:

 SWAP(x, y, float); 

Si está contento de usar extensiones específicas de gcc, puede mejorar esto de la siguiente manera:

 #define SWAP(x, y) do { typeof(x) SWAP = x; x = y; y = SWAP; } while (0) 

y entonces solo sería:

 SWAP(a, b); 

o:

 SWAP(x, y); 

Esto funciona para la mayoría de los tipos, incluidos los punteros.

Aquí hay un progtwig de prueba:

 #include  #define SWAP(x, y) do { typeof(x) SWAP = x; x = y; y = SWAP; } while (0) int main(void) { int a = 1, b = 2; float x = 1.0f, y = 2.0f; int *pa = &a; int *pb = &b; printf("BEFORE:\n"); printf("a = %d, b = %d\n", a, b); printf("x = %f, y = %f\n", x, y); printf("pa = %p, pb = %p\n", pa, pb); SWAP(a, b); // swap ints SWAP(x, y); // swap floats SWAP(pa, pb); // swap pointers printf("AFTER:\n"); printf("a = %d, b = %d\n", a, b); printf("x = %f, y = %f\n", x, y); printf("pa = %p, pb = %p\n", pa, pb); return 0; } 

GMan comenzó este bash, para codificar esto en combinación de una función en inline y una macro. Esta solución supone que tiene un comstackdor de C moderno que admite C99, ya que utiliza un literal compuesto :

 inline void swap_detail(void* p1, void* p2, void* tmp, size_t pSize) { memcpy(tmp, p1, pSize); memcpy(p1, p2, pSize); memcpy(p2 , tmp, pSize); } #define SWAP(a, b) swap_detail(&(a), &(b), (char[(sizeof(a) == sizeof(b)) ? (ptrdiff_t)sizeof(a) : -1]){0}, sizeof(a)) 

Esto tiene las siguientes propiedades:

  • Evalúa cada uno de a y b solo una vez.
  • Tiene un control de tiempo de comstackción para los tamaños correctos.
  • No tiene un problema de nomenclatura con una variable oculta.
  • El tamaño de la variable temporal se calcula en tiempo de comstackción, por lo que el literal compuesto no es una matriz dinámica.

El molde (ptrdiff_t) es necesario para que el -1 no se promocione silenciosamente a SIZE_MAX .

Esta solución todavía adolece de dos inconvenientes:

  1. No es tipo seguro. Solo verifica los tamaños de los tipos, no su semántica. Si los tipos son diferentes, digamos un double de tamaño 8 y un uint64_t , tiene problemas.

  2. Las expresiones deben permitir que & operador sea aplicable. Por lo tanto, no funcionará en las variables que se declaran con la clase de almacenamiento de register .

En pocas palabras: no se puede hacer una macro de intercambio genérico en C, al menos, no sin algún riesgo o dolor de cabeza . (Ver otras publicaciones. La explicación se encuentra debajo).

Las macros son agradables, pero el problema con el que se encontrará con el código real sería problemas con el tipo de datos (como ha indicado). Además, las macros son “tontas” de alguna manera. Por ejemplo:

Con su ejemplo macro #define swap(x,y) { x = x + y; y = x - y; x = x - y; } #define swap(x,y) { x = x + y; y = x - y; x = x - y; } #define swap(x,y) { x = x + y; y = x - y; x = x - y; } , swap(++x, y) convierte en { ++x = ++x + y; y = ++x - y; ++x = ++x - y;} { ++x = ++x + y; y = ++x - y; ++x = ++x - y;} { ++x = ++x + y; y = ++x - y; ++x = ++x - y;} .

Si ejecutó int x = 0, y = 0; swap(++x, y); int x = 0, y = 0; swap(++x, y); obtendrías x=2, y=3 vez de x=0, y=0 . Además de esto, si alguna de las variables temp en su macro aparece en su código, podría encontrarse con algunos errores molestos.

La funcionalidad que está buscando se introdujo en C ++ como plantillas. Lo más cercano que puede obtener en C es usar una función en línea para cada tipo de datos imaginables o una macro decentemente compleja (vea los problemas macro anteriores y las publicaciones anteriores).

Esto es lo que parece la solución que usa plantillas en C ++:

 template inline void swap(T &x, T &y) { T tmp = x; x = y; y = tmp; } 

En C necesitarías algo como:

 inline void swap_int(int *x, int *y) { /* Code */ } inline void swap_char(char *x, char *y) { /* Code */ } // etc. 

o (como se mencionó algunas veces) una macro bastante compleja que tiene el potencial de ser peligrosa.

Esto no necesariamente funciona bien para int según el estándar. Imagina el caso donde y son INT_MAX e INT_MAX-1 respectivamente. La primera statement de adición daría como resultado un desbordamiento firmado que no está definido por el estándar.

Una implementación más confiable de la macro swap para int sería el algoritmo de intercambio XOR

En serio, ¿cuántos swaps tienes que hacer en tu código que vale la pena todos los dolores de cabeza que surgen en este hilo con las soluciones dadas? Quiero decir, esta no es una estructura de código de 10 líneas complicada y propensa a errores, es un idioma bien conocido con una variable temporal y tres asignaciones simples. Escríbalo donde lo necesite, incluso en una línea si desea ahorrar espacio:

 function foo () { int a, b, tmp; ... tmp = a; a = b; b = tmp; .... } 

O use una macro “local” donde a y b son más complejos.

 #define SWAP(t,a,b) ( (t) = (a), (a) = (b), (b) = (t) ) function foo (pointer) { int tmp; ... SWAP(tmp, pointer->structure.array[count+1], pointer->structure.array[count+2]); ... } #undef SWAP