Wykład 6
Wskaźniki Wskaźnik nie przechowuje wartości zmiennej ale, podobnie jak tablica, wskazuje miejsce w pamięci, w którym znajduje się zmienna danego typu. W poniższym przykładzie symbol * pomiędzy int, a wskaz oznacza, że wskaz jest wskaźnikiem do zmiennej typu int (a nie zmienną typu int). int *wsk; //deklaracja wskaźnika wsk=new int; //przydzielenie zmiennej miejsce w pamięci *wsk=5; //zapisanie liczby 5 w tym miejscu pamięci cout << *wsk << endl; delete wsk; //zwolnienie pamięci wskazywanej przez wskaźnik Polecenie delete zwalnia miejsce w pamięci - nie będzie ona zatem zajmowana przez cały czas wykonywania programu, co ma miejsce przy normalnym deklarowaniu zmiennych.
Wskaźniki - tablice Zmienna tablicowa jest wskaźnikiem do obszarów pamięci. int* T; int n; cin >> n; T = new int[n]; for (int i=0; i<n; i++) cin>>t[i]; for (i=0; i<n; i++) cout << T[i] << ", "; delete [] T;
Adresy Operator & adresu występuje przy deklaracji zmiennej zwanej referencją. Poniżej zmienna alias podszywa się pod zmienną x typu int. int x = 15; int & alias = x; Zamiast operatora * wyłuskania (dereferencji) w poniższym przypadku int x = 35; int * wsk = & x; *wsk = 45; wygodniej użyć operatora referencji int x = 35; int& alias = x; alias = 45;
Wskaźniki, funkcje Funkcję skracając ułamek (zwracającą dwie wartości: licznika i mianownika) możemy napisać używając wskaźników. void skrac1(int *a, int *b){ int d=nwd(*a,*b); *a=*a/d; *b=*b/d; } Wywołamy ją poleceniem skrac1(&licz, &mian), a wynik wypiszemy poleceniem cout << licz << "/" << mian << endl; Zauważmy, że funkcja sortowania szybkiego nie zawiera polecenia zwrócenia wartości return, a jednak zmienia wartości w tablicy. Wynika to stąd, że nazwa tablicy jest wskaźnikiem stałym, a nie nazwą zmiennej.
Referencje, funkcje Do skracania ułamków możemy użyć także referencji void skrac2(int &a, int &b){ int d=nwd(a,b); a=a/d; b=b/d; } W tym przypadku funkcję wywołujemy poleceniem skrac2(licz, mian).
Struktury Poleceniem struct możemy stworzyć strukturę złożoną ze zmiennych. Stwórzmy strukturę o nazwie autor. struct autor{ string imie, nazwisko, tytul; int rok; }; //deklarujemy zmienne //czyli pola struktury
Struktury Możemy teraz zadeklarować zmienne typu autor oraz nadać im cechy. autor os1, os2; os1.imie = "Thomas"; os1.nazwisko = "Cormen"; os1.tytul = "Wprowadzenie do algorytmow"; os1.rok = 1997; cin >> os2.imie >> os2.nazwisko >> os2.tytul >> os2.rok; Zmiennym os1, os2 odpowiadają dane wpisanych osób. Możemy wybrane z nich wypisać pisząc cout << "Pierwszy autor nosi nazwisko " << os1.nazwisko << endl; cout << "Drugi autor nosi imię " << os2.imie << endl;
Klasy, metody Tworząc strukturę możemy dodać do niej funkcje - taką strukturę nazywamy klasą. struct autor{ string imie, nazwisko, tytul; //deklarujemy zmienne int rok; //czyli pola klasy void napisz(); //określamy funkcję }; //czyli metodę klasy void autor::napisz(){ cout << "Autorem " << tytul << " jest " << nazwisko << endl; } Teraz po zdefiniowaniu zmiennych os1 i os2 możemy wypisać dane, np. os1.napisz();
Struktury, funkcje Jeśli stworzymy strukturę z dwoma polami struct para {int licznik; int mianownik;}; to będziemy mogli za jej pomocą napisać funkcję skracającą ułamki. para skrac3(int a, int b){ int d=nwd(a,b); para p; //deklaracja zmiennej p typu para p.licznik = a/d; p.mianownik = b/d; return p; } Jest to funkcja typu para. Aby skrócić ułamek x y napiszemy para p = skrac3(x, y); cout << p.licznik << / << p.mianownik << endl;
Rozszerzony algorytm Euklidesa Za pomocą rozszerzonego algorytmu Euklidesa możemy dla danych liczb całkowitych a, b znaleźć współczynniki całkowite x, y tak, aby NWD(a,b) = ax + by. W szczególności jeśli a i b są względnie pierwsze otrzymujemy ax + by = 1. (Oznacza to, że jeśli NWD(a,n) = 1, to ax = 1(modn), czyli x jest multiplikatywną odwrotnością a modulo n.)
Rozszerzony algorytm Euklidesa Algorytm zapisany w pseudokodzie wygląda następująco ext_eukl(a,b) 1 if b=0 2 then return (a,1,0) 3 else 4 (d,x,y ) <- ext_eukl(b,a mod b) 5 (d,x,y) <- (d,y,x -[a/b]y ) 6 return (d,x,y) Zauważmy, że bx + a(mod b)y = bx + ay b[a/b]y = ay + b(x [a/b]y ) = ax + by, czyli algorytm daje prawidłowy wynik.
Rozszerzony algorytm Euklidesa Aby zaimplementować go w języku C++ stworzymy strukturę złożoną z trzech liczb całkowitych struct Trojka{ int d; int x; int y; }; a następnie napiszemy funkcję rekurencyjną
Rozszerzony algorytm Euklidesa Trojka ext_eukl(int a, int b){ Trojka t; if(b==0){ t.d = a; t.x = 1; t.y = 0; } else{ t=ext_eukl(b, a%b); int tymczas=t.x; t.x = t.y; t.y = tymczas-(a/b)*t.y; } return t; }
Przykładowe zadania Po wczytaniu liczb a,b możemy wywołać funkcję i wypisać wynik na ekran Trojka rozw = ext_eukl(a, b); cout <<"x="<<rozw.x<<", y="<<rozw.y<<", d="<<rozw.d<<endl; Zadanie Napisz funkcję sumującą dowolne dwa ułamki. Zadanie Napisz program, który dla wczytanych liczb całkowitych a, n znajduje multiplikatywną odwrotność a modulo n o ile ta istnieje.