====== Wskaźniki i referencje ====== //In C%%++%% it's harder to shoot yourself in the foot, but when you do, you blow off your whole leg.// To laboratorium jest przypomnieniem informacji o wskaźnikach, które powinny zostać przyswojone w poprzednim semestrze na //"Językach i Metodach Programowania I"//. Z racji, że nie jest to „regularne” laboratorium, za rozwiązanie zadań nie będą przyznawane plusy/punkty. ===== Wskaźniki ===== Wskaźniki są zmiennymi przechowującymi adresy pamięci innych zmiennych. Operatory związane ze wskaźnikami: * Operator wyłuskania //*// - zastosowany do wskaźnika wyłuskuje zmienną znajdującą się pod adresem przechowywanym przez wskaźnik * Operator adresu //&// - zastosowany do zmiennej wybiera adres pamięci pod którym przechowywana jest wartość zmiennej. {{.:pointers.png |Wskaźniki}}#include using namespace std; int main(void){ int liczba = 10; int *pointer = &liczba; cout << liczba << endl; // wyświetli 10 cout << *pointer << endl; // wyświetli 10 cout << &liczba << endl; // wyświetli (rys.) 0xbf8d6d4c cout << pointer << endl; // wyświetli (rys.) 0xbf8d6d4c return 0; } ==== Wskaźniki i const ==== Stosowanie słowa kluczowego **const** w kontekście wskaźników, może być powodem błędów: * //const int * ptr = &zmienna;// - wskaźnik do stałej wartości. Wskaźnikowi będzie można przypisać inny adres, ale wartość spod adresu //&zmienna// będzie stała. * //int * const ptr = &zmienna;// - stała będzie wartość wskaźnika. Będzie można zmienić wartość //zmiennej//, ale nie będzie można przypisać danemu wskaźnikowi innego adresu. Wskaźnikowi takiemu musi zostać przypisana wartość w momencie deklaracji. ==== Wskaźnik void* ==== Może przechowywać wartość wskaźnika dowolnego typu. Nie może jednak podlegać dereferencji - wcześniej konieczne jest rzutowanie na typ konkretny. int zmienna; int* ptr = &zmienna; void* vptr = ptr; //NIEPOPRAWNIE cout << "Wartość zmiennej to: " << *vptr << endl; //Poprawnie: cout << "Wartość zmiennej to: " << *(int*)vptr << endl; ==== Wskaźniki do funkcji ==== Podobnie jak nazwa tablicy jest wskaźnikiem do jej pierwszego elementu, tak nazwa funkcji jest początkowym adresem w pamięci kodu dokonywującego zadanie. Wskaźniki do funkcji najczęściej przekazywane są jako parametry innych funkcji. Definicja funkcji przyjmującej jako parametr wskaźnik do innej funkcji: // funkcja foo przyjmuje jako parametr wskaźnik do funkcji zwracającej int // oraz przyjmującej dwa parametry typu int void foo(int (*fooptr)(int,int)){ // Wywołanie funkcji przekazanej jako parametr cout << "Wynik : " << (*footptr)(10,10) << endl; } ==== Arytmetyka wskaźników ==== Do wskaźników można stosować podstawowe operatory matematyczne: * wskaźniki można zwiększać i zmniejszać (operatory: %%++%% i --) * można do nich dodawać, lub odejmować liczby całkowite (operatory + i -) * można odejmować od siebie dwa wskaźniki Należy pamiętać, że inkrementowanie wskaźnika nie zwiększa wartości adresu o 1, ale o rozmiar wskaźnika. Jeśli jest to wskaźnik do typu int, to standardowo o 4 bajty, jeśli double, to o 8 bajtów, etc. ===== Referencje ===== Są to aliasy zmiennych. Referencja wskazuje na dokładnie tą sama komórkę pamięci co "oryginalna" zmienna. Referencja musi być zainicjalizowana w momencie deklaracji! Jedyne sensowne zastosowanie referencji, to przekazywanie parametrów do funkcji poprzez referencję. To samo jednak można osiągnąć używając wskaźników... {{.:references.png |Referencje}}#include using namespace std; int main(void){ int liczba = 10; int &ref = liczba; cout << liczba << endl; // wyświetli 10 cout << ref << endl; // wyświetli 10 cout << &liczba << endl; // wyświetli (rys.) 0xbf8d6d4c cout << &ref << endl; // wyświetli (rys.) 0xbf8d6d4c return 0; } ======Ćwiczenia====== - Napisz 3 funkcje które w swoim ciele będą zmieniały wartość przekazanego do nich parametru typu int. Funkcje mogą na przykład sprawdzać czy dana liczba jest liczbą pierwszą. Przetestuj działanie funkcji. Jaka jest różnica pomiędzy funkcją przyjmującą wskaźnik a funkcja przyjmującą referencję? Napisz następujące wersje funkcji: * pobierającą referencję * pobierającą wskaźnik * pobierająca zmienną - Skompiluj poniższy kod. Co jest źle:#include using namespace std; int * potega(int liczba, int potega){ int* wynik = &liczba; for(int i = 1; i < potega; i++) *wynik *= liczba; return wynik; } int main(void){ cout << *(potega(3,3)) << endl; } - Nazwa tablicy jest wskaźnikiem do jej pierwszego elementu. Napisz metodę wyświetlającą tablicę dwuwymiarową za pomocą jednej pętli **for**, bez używania operatora **[]** dostępu do elementów tablicy. Deklaracja funkcji powinna wyglądać następująco: void showTable(int tab[][5], int rows, int cols); - Sortowanie bąbelkowe. Napisz funkcję implementującą algorytm sortowania bąbelkowego, która jako parametry przyjmuje następujące elementy: * int * tablica, * int length, * int (*porownanie)(int,int) - zaimplementuj dwie wersje funkcji //porównanie// jedna porównującą malejąco, a drugą rosnąco. Przetestuj działanie programu. - Wskaźniki do funkcji i funkcja sortująca. W bibliotece standardowej istnieje funkcja implementująca algorytm sortowania //quicksort//. Aby użyć tej funkcji konieczne jest napisanie własnej funkcji porównującej i przekazanie jej do metody sortującej. Przeczytaj rozdział poświęcony tej funkcji: [[http://www.cppreference.com/wiki/c/other/qsort|qsort]]. Napisz strukturę, która będzie zawierać następujące pola: * char imie[10]; * char nazwisko[10]; * short rok_urodzenia; Napisz program umożliwiający wypełnienie tablicy kilkoma elementami stworzonej struktury. Następnie wykorzystując funkcje **qsort** napisz program sortujący tablicę struktur według roku urodzenia lub według nazwiska.