Laboratorium 7
Wskaźniki, rekurencja
Wskaźniki
Deklaracja wskaźnika, alokacja pamięci
Wskaźnik jest to adres pod którym przechowywana jest zmienna określonego typu. Deklaracja ma postać:
typ *nazwa;
np. int *wsk;. Aby można było z tym wskaźnikiem zrobić coś
sensownego można przypisać mu adres istniejącej zmiennej. Wykorzystuje
się do tego operator pobrania adresu &. Aby otrzymać lub
zmienić wartość na którą wskazuje dany wskaźnik należy użyć operatora
*. Przykładowo:
int z;
int *wsk=&z;
*wsk=1; // zmienna z będzie miała wartość 1
Jeśli wsk jest wskaźnikiem danego typu, to można na nim
wykonywać operacje arytmetyczne. I tak wsk+1 będzie wskazywał
na kolejną zmienną danego typu w pamięci itd.
W języku C++ jak i w C istnieje ścisła zależność pomiędzy tablicami a
wskaźnikami. Można powiedzieć, że tablica jest wskaźnikiem stałym (nie
prawidłowa jest na przykład instrukcja T++, gdzie T
jest tablicą).
Przykładowo:
int T[3]={1,2,3};
int *wsk=T;
*wsk=3; // równoważnie T[0]=3
wsk++;
*wsk=2; // T[1]=2
wsk++;
*wsk=1; // T[2]=1
Wskaźnikowi można także dynamicznie (tzn. w trakcie działania programu)
przydzielić pamięć. Wykorzystujemy w tym celu operator new:
int *wsk=new int; // alokacja nowej zmiennej typu int
double *wd=new double[10]; // alokacja tablicy
Aby zwolnić wcześniej przydzieloną pamięć wykorzystujemy operator
delete:
delete wsk;
delete []wd;
Tablica wskaźników a wskaźnik na tablice
int T1[10][10];
int (*W1)[10];
int **W2;
int *T2[10];
Po takich deklaracjach T1 jest tablicą dwuwymiarową 10x10,
W1 wskaźnikiem na tablicę 10-elementową,
W2 wskaźnikiem na
wskaźnik, zaś T2 tablicą 10 wskaźników.
Możliwe są więc przypisania:
W2=T2; // T2 jest tablicą wskaźników, a więc wskaźnikiem na wskaźnik
W1=T1; // T1 jest tablicą tablic, a więc wskaźnikiem na tablicę
Ale nie możliwe są przypisania W2=T1 lub W1=T2.
Porównaj dwa programy:
Rekurencja
Symbol Newtona
Newton2.
Zastanów się, który z programów (ten czy z trzeciego laboratorium)
jest bardziej efektywny?
Generowanie permutacji
Permutacje
Sortowanie Merge Sort
Algorytm sortowania MergeSort zadanego wektora n-elementowego:
- Jeśli n=1, to wektor jest posortowany, koniec;
- Dla n>1 podziel wektor na dwa "mniej więcej" równe wektory w1 i w2;
- Posortuj wektory w1 i w2 algorytmem MergeSort;
- Scal wektory w1 i w2 do wektora posortowanego, wykorzystując, że są one posortowane.
Program sortujący wektor liczb całkowitych: MergeSort.
Krzywa Kocha
Definiujemy rekurencyjnie następujący sposób ciąg krzywych K(k):
- Jeśli k=0, to K(0) jest odcinkiem;
- Dla k>0 K(k) powstaje przez odpowiednie połączenie czterech kopii krzywych K(k-1) (patrz ilustracja -
poszczególne kopie krzywych stopnia o jeden mniejszego wyróżniono różnymi kolorami).
Program generujący rekurencyjnie krzywą Kocha zadanego stopnia:
Koch.cpp
Uruchom ten program rysując kolejno krzywe stopnia 0, 1, 2, itd.
Trójkąt Sierpińskiego
Definiujemy rekurencyjnie ciąg podzbiorów płaszczyzny TS(k):
- TS(0) to wypełniony trójkąt;
- Dla k>0 zbiór TS(k) powstaje poprzez sklejenie 3 mniejszych kopii TS(k-1) (patrz rysunek).
Program generujący trójkąt Sierpińskiego zadanego stopnia:
sierp.cpp
Uruchom ten program rysując kolejno trójkąt stopnia 0, 1, 2, itd.