Zaimplementuj ogólne makro swap w C [duplikat]

Możliwy duplikat:
czy istnieje odpowiednik std:: swap () w c

Cześć wam,

Próbowałem napisać ogólne makro swap w C i moje makro wygląda tak:

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

Działa dobrze dla liczb całkowitych i pływaków, ale nie jestem pewien, czy jest w nim jakiś haczyk. Co jeśli przez ogólne makro oznaczają one zamianę wskaźników, znaków itp.? Czy ktoś może mi pomóc w pisaniu ogólnego makra do zamiany każdego wejścia ?

Thanks

Author: Community, 2010-10-21

6 answers

To działa dobrze tylko z liczbami całkowitymi.

W przypadku float 'ów nie powiedzie się (np. spróbuj uruchomić go z bardzo dużym float' em i bardzo małym float ' em).

Proponuję coś w następujący sposób:

#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 jest dość zoptymalizowane, gdy ilość do skopiowania jest znana w czasie kompilacji. Nie ma też potrzeby ręcznego przekazywania nazwy typu ani używania rozszerzeń specyficznych dla kompilatora.

 74
Author: adamk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-21 08:28:12

Możesz zrobić coś takiego:

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

Które następnie wywołujesz tak:

SWAP(a, b, int);

Lub:

SWAP(x, y, float);

Jeśli chcesz używać rozszerzeń specyficznych dla gcc, możesz to poprawić w następujący sposób:

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

A wtedy będzie:

SWAP(a, b);

Lub:

SWAP(x, y);

Działa to dla większości typów, w tym wskaźników.

Oto program testowy:

#include <stdio.h>

#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;
}
 53
Author: Paul R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-22 08:16:30

GMan rozpoczął tę próbę, aby zakodować to w kombinacji funkcji inline i makra. To rozwiązanie zakłada, że masz nowoczesny kompilator C, który obsługuje C99, ponieważ używa literał złożony :

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

Ma następujące właściwości:

  • ocenia każdy z a i b tylko raz.
  • ma sprawdzanie czasu kompilacji dla prawidłowe rozmiary.
  • nie ma problemu z nazwami z ukrytym zmienna.
  • wielkość zmienna tymczasowa to obliczane w czasie kompilacji, więc literał złożony nie jest dynamiczny / align = "left" /

Obsada (ptrdiff_t) jest potrzebna, aby -1 nie była po cichu promowana do SIZE_MAX.

To rozwiązanie nadal ma dwie wady:

  1. Nie jest bezpieczny. Sprawdza tylko dla wielkości typów, nie ich semantyka. Jeśli typy różnią się, powiedzmy a double wielkości 8 i a uint64_t, masz kłopoty.

  2. Wyrażenia muszą pozwalać na & operator, który ma zastosowanie. Tak więc nie będzie działać na zmiennych, które są zadeklarowane za pomocą register Klasa przechowywania.

 11
Author: Jens Gustedt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-21 13:07:18

Mówiąc najprościej: nie można zrobić ogólnego makra wymiany w C, przynajmniej nie bez ryzyka lub bólu głowy . (Zobacz inne posty. Wyjaśnienie znajduje się poniżej.)

Makra są ładne, ale problem, który napotkasz z rzeczywistym kodem, to problemy z typem danych (jak już wspomniałeś). Co więcej, makra są w pewnym sensie" głupie". Na przykład:

Z przykładowym makrem #define swap(x,y) { x = x + y; y = x - y; x = x - y; }, swap(++x, y) zamienia się w { ++x = ++x + y; y = ++x - y; ++x = ++x - y;}.

Jeśli biegniesz int x = 0, y = 0; swap(++x, y); dostaniesz x=2, y=3 zamiast x=0, y=0. Ponadto, jeśli którakolwiek ze zmiennych tymczasowych w makrze pojawi się w kodzie, możesz napotkać denerwujące błędy.

Funkcjonalność, której szukasz, została wprowadzona w C++ jako szablony. Najbliżej można uzyskać w C jest użycie funkcji inline dla każdego typu danych, które można sobie wyobrazić lub przyzwoicie złożonego makra (Zobacz poprzednie problemy z makrami i poprzednie posty).

Oto jak wygląda rozwiązanie wykorzystujące szablony w C++:

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

In C you ' d need something like:

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

Lub (jak wspomniano kilka razy) dość złożone makro, które może być niebezpieczne.

 9
Author: Mr. Llama,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-22 02:00:26

To niekoniecznie działa dobrze dla int według standand. Wyobraź sobie przypadek, w którym x i y były odpowiednio INT_MAX i INT_MAX-1. Pierwsza instrukcja addition skutkowałaby podpisanym przepełnieniem, które jest niezdefiniowane przez standard.

Bardziej niezawodną implementacją makra swap dla int byłby algorytm XOR swap

 0
Author: JaredPar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-20 21:19:27

Poważnie, ile swapów musisz zrobić w swoim kodzie, że warto wszystkie bóle głowy pojawiające się tutaj w tym wątku z podanymi rozwiązaniami? Chodzi mi o to, że nie jest to 10-liniowa skomplikowana i podatna na błędy struktura kodu, jest to dobrze znany idiom z jedną zmienną tymczasową i trzema prostymi przydziałami. Napisz go tam, gdzie go potrzebujesz, nawet w jednej linii, jeśli chcesz zaoszczędzić miejsce:

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

Lub użyj "lokalnego" makra, gdzie a i b są bardziej złożone.

#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
 -2
Author: Secure,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-21 07:55:55