Wariacje na temat zamiany miejscami dwóch wartości
Zamiana dwóch liczb (a=5 b=89) miejscami, że (a=89 b=5), jest prostą rzeczą do zaprogramowania. Ale nawet tak prostą, rzeczy można skomplikować, do takiego stopnia, że wymagane jest wprowadzenie komentarzy.
Algorytm jest prosty:
- Utwórz zmienną pomocniczą tmp
- Przypisz do tmp wartość a
- Przypisz do a wartość b
- Przypisz do b wartość tmp
void swap_int(int* a, int*b)
{
int tmp =*a;
*a=*b;
*b=tmp;
}
/* Wywołanie funkcji */
int main(){
int a=15,b=75;
swap_int(&a,&b);
}
Jednak powyższe rozwiązanie ma dwie wady:
- Funkcja nie działa dla każdego typu danych
- Wywołanie funkcji jest spowolnione, ponieważ jest funkcją. (tzw. koszt wywołania funkcji, związany z assemblerową instrukcją call)
#define SWAP(a, b, size)
do
{
register size_t __size = (size);
register char *__a = (a), *__b = (b);
do
{
char __tmp = *__a;
*__a++ = *__b;
*__b++ = __tmp;
} while (--__size > 0);
} while (0)
#define TWO_PRINTF()
printf("%s","Hello ");
printf("%s","World")
if(display_hello())
TWO_PRINTF();
if(display_hello())
printf("%s","Hello ");
printf("%s","World");
Ten boczny wywód na temat makr, jest ważny dla zrozumienia dlaczego występuje pętla do{…]while(0). Taka pętla wykona się zawsze jeden raz ponieważ warunek sprawdzający jest zawsze fałszywy. Pętla posiada kilka ważnych cech dla działania marka
- Pozwala uniknąć błędu z if’em który został wcześniej opisany
- Pozwala na zadeklarowanie zmiennych w akurat tym miejscu programu
- Dodaje wartość czysto stylistyczną wywołanie makra musi się kończyć średnikiem, więc makro wygląda jakby było normalną funkcją.
Ciekawym trickiem wykorzystanym w makrze jest utworzenie kolejnych zmiennych o nazwach __a i __b ponieważ to zapobiega wpadnięciu w pułapkę kiedy do makra poda się na przykład SWAP(pa++,pb++,sizeof(char)) wtedy wskaźnik może zostać przysunięty nie tylko jeden raz.
int a,b;
int *pa,*pb;
pa=&a;
pb=&b;
SWAP((char *) pa,(char *)pb,sizeof(int));
Jak widać stworzenie wydajnej i działającej na wszystkich typach funkcji w języku C jest nieco skomplikowane.
W językach wysokiego poziomu, np Pythonie wystarczy jedna linijka kodu:
a, b = b, a
Powyższa linijka działa dla każdego typu zmiennych od liczb całkowitych, przez liczby zmiennoprzecinkowych po zmienne typu string