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
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.
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;
}
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
ib
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:
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 auint64_t
, masz kłopoty.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.
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.
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
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
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