1 Wska¹niki. Wska¹nik jest obiektem przechowuj cym adres (z pami ci) przypisanej do niego zmiennej. int a; int *b; a = 11; cout << "a = " << a; b = &a; /*w tej linijce wskaznik jest laczony z adresem zmiennej*/ cout <<" wartosc zmiennej wskazywanej przez b jest rowna " << *b; *b = 14; cout << "a = " << a; Wska¹niki mo»na zastosowa do: { ulepszenia pracy z tablicami, { funkcji mog cych zmienia warto± przesyªanych do nich argumentów, { dost pu do specjalnych komórek pami ci, { rezerwacji obszarów pami ci. 1.1 Wska¹nik typu VOID. Wska¹nik jak wspomniano wy»ej sªu»y do wskazywania adresu jakiego± miejsca (obiektu) w pami ci. Deniuj c wska¹nik nale»y pami ta by jego typ zgadzaª si z typem wskazywanego obiektu. Rozumie mo»na to,»e tworz c wska¹nik do obiektu posiadamy wiedz na temat jego typu. Stosuj c wska¹nik typu void ±wiadomie rezygnujemy z tej ÿwiedzy" a nasz wska¹nik automatycznie traci mo»liwo± odczytania miejsca na które wskazuje. Da si t mo»liwo± ÿodzyska " przez zastosowania rzutowania: 1
int a; void *wsk; (int*)wsk = &a; Taki upo±ledzony wska¹nik mo»e jednak posªu»y do wskazywania na inne wska¹niki. Je±li chcemy by wska¹nik o okre±lonym typie wskazywaª do wska¹nika typu void nale»y wykona rzutowanie. Je±li chcemy wy±wietli warto± zmiennej lub wska¹nika do którego wskazuje wska¹nik typu pustego równie» nale»y wykona rzutowanie. int masa=135, *wag, wyb=0; float km = 14.96, *moc; void *wsk; moc = &km; wag = &masa; cout << "Dobierz motocykl o pojemnosci do 125cm^3 dla siebie\n" << "Jaki parametr jest dla Ciebie najistotniejszy?\n" << "\t1. Masa\n" << "\t2. Moc silnika\n" << "Wprowadz swoj wybor: "; cin >> wyb; if (wyb == 1) { wsk = wag; cout << "Motocykl dla Ciebie to: Yamacha Virago 125\n" <<"Jego masa to: " << *(int*)wsk << "Kg" << endl; if (wyb == 2) { wsk = moc; cout << "Motocykl dla Ciebie to: Honda VT Shadow 125\n" <<"Jego moc to: " << *(float*)wsk << "KM" << endl; 2
1.2 Wska¹niki a tablice. Zaªó»my,»e mamy nast puj c sytuacj : int *wsk; int tab[5]; wsk=&tab[n]; W ten sposób przypisali±my wska¹nik wsk do n-tego elementu tablicy tab. Je- ±li chcemy przypisa wska¹nik do elementu numer zero, wystarczy po prostu wpisa nazw tablicy (nazwa tablicy zawsze wskazuje na jej zerowy element). wsk=&tab; Je±li w trakcie pracy programu chcemy by wska¹nik wskazywaª na kolejny element tej tablicy wystarczy zastosowa poni»sz instrukcj : wsk = wsk+1; //lub wsk++; Aby przesun wska¹nik o n elementów wystarczy wpisa : wsk += n; W przypadku innych typów ni» int nie musimy si przejmowa,»e powy»sza metoda przeskoczy nam do kolejnego bitu. Dzi ki temu,»e kompilator wie jakiego typu mamy wska¹nik, wie równie» o ile bitów musi si przesun by znale¹ kolejny element tablicy (Rys. 1). Rysunek 1: Przemieszczanie si za pomoc wska¹ników po zawarto±ci tablicy. ródªo: Symfonia C++ J. Gr bosz. 3
float tab[4] = {7.23, 12.46, 16.12, 23.28; float *wsk; wsk=tab; cout << "Najblizszy pociag z Krakowa do " << "Krainymlekiemimiodemplynacej odjezdza o: " << *wsk << endl; wsk=wsk+1; cout << "Nastepny pociag odjezdza o: " << *wsk << endl; wsk++; cout << "Kolejny pociag odjezdza o: " << *wsk << endl; wsk+=1; cout << "Ostatni pociag odjezdza o: " << *wsk << endl; 1.3 Wska¹nik jako argument funkcji. Wska¹ników jako argumentów funkcji u»ywa si w celu zmiany warto±ci obiektu przez funkcj. Z poprzednich zaj wiemy,»e najprostszym sposobem by to osi gn jest zastosowanie funkcji o po» danym typie i zwrócenie pewnej warto±ci. Niestety metoda ta pozwala nam na zwrócenie tylko jednej warto±ci. W sytuacjach, gdy chcemy by funkcja wpªyn ªa na wi cej ni» jeden obiekt pomagaj nam wska¹niki. void pizza(int *lpi, float *gotowka); int liczba_pizzy = 0; float pln = 134.28; cout << "Obecnie posiadasz " <<liczba_pizzy << " pizze.\n"; cout << "Masz przy sobie " << pln << " PLN." << endl; cout << "Czas zamowic pizze!" << endl; 4
pizza(&liczba_pizzy, &pln); cout << "Twoja dostawa nadeszla\n"; cout << "Teraz posiadasz " << liczba_pizzy << " pizze.\n"; cout << "Zostalo Ci " << pln << " PLN." << endl; void pizza (int *lpi, float *gotowka) { *lpi = 2; *gotowka = 62.47; W powy»szym kodzie udaªo si zmieni warto±ci dwóch zmiennych za pomoc jednej funkcji. Maªo tego, udaªo si to zrobi wykorzystuj c funkcj void, czyli tak która nie zwraca»adnych warto±ci. Udaªo si to osi gn poprzez zastosowanie wska¹ników. Normalnie do funkcji przesyªana jest kopia obiektu, jednak»e w tym przypadku do funkcji przesªany zostaª konkretny adres konkretnego obiektu i przypisano go do odpowiedniego wska¹nika. Dzi ki temu modykuj c wska¹nik, tak naprawd zmodykowana zostaªa warto± obiektu. 1.4 Zastosowanie wska¹nika przy dost pie do konkretnych komórek pami ci. Istnieje mo»liwo± przypisania wska¹nikowi konkretnej komórki pami ci. Stosuje si to na przykªad gdy do danego adresu przesyªane s dane z jakiego± czujnika, lub innego urz dzenia zewn trznego i komórka ta nie jest w programie nazywana. Zaªó»my,»e chcemy by wska¹nik wskazywaª na komórk 45634. By przypisa do tego adresu wska¹nik wystarczy: wsk = 45634; Istniej niestety sytuacje w których przypisanie wska¹nika do konkretnego adresu nie jest takie proste. Wszystko zale»y od typu komputera lub systemu operacyjnego. Informacje na ten temat mo»na znale¹ w dokumentacji konkretnych kompilatorów. 5
2 Dynamiczny przydziaª pami ci. 2.1 Rezerwacja obszarów pami ci. W celu rezerwacji obszarów w pami ci (np. do stworzenia tablicy dynamicznej) posªu»ymy si operatorem new (w j zyku c odpowiednikiem byª by malloc). By wyczy±ci dany obszar wykorzystywany jest operator delete (odpowiednik c - free). Zaªó»my,»e mamy zdeniowany wska¹nik: int *wsk; Aby stworzy nowy obiekt w pami ci stosujemy nast puj c instrukcj : wsk = new char; Dzi ki temu stworzyli±my w pami ci nowy obiekt typu char. Obiekt ten nie ma nazwy, ale wska¹nik wsk posiada zapisany jego adres w pami ci. W celu wyczyszczenia pami ci z tego obiektu u»ywamy: delete wsk; Cechy stworzonych w ten sposób obiektów: { obiekt taki istnieje od momentu stworzenia (zastosowanie new) do momentu usuni cia, (zastosowanie delete), to programista decyduje o dªugo±ci jego ÿ»ycia" { obiekt ten nie ma nazwy, mo»emy nim operowa jedynie przez wska¹nik do niego wskazuj cy, {takiego obiektu nie obowi zuj standardowe zasady zakresu wa»no±ci. Je±li tylko istnieje wska¹nik do niego wskazuj cy mo»emy z niego skorzysta, { obiekty tworzone w ten sposób s dynamiczne, dlatego zawsze b d przyjmowa ±mieciowe warto±ci, nale»y o tym pami ta i zadba o wpisanie w nie sensownych warto±ci. char * tworzobiekt (void); /* tworzymy tu funkcje wywolywana bez argumentow, ktora zwracac bedzie wskaznik typu char */ char *wsk1, *wsk2, *wsk3; 6
cout << "Teraz nastapi akt stworzenia!\n"; wsk1 = tworzobiekt(); wsk2 = tworzobiekt(); wsk3 = tworzobiekt(); *wsk1 = 'a'; *wsk2 = 'b'; cout << "\nstworzylismy trzy nowe obiekty.\n" << "Do dwoch przypisano wartosci: "<< *wsk1 << *wsk2 << endl << "W trzecim widzimy same smieci: " << *wsk3 << endl; delete wsk1; delete wsk2; delete wsk3; char * tworzobiekt (void) { char *w; cout << endl << "Tworze obiekt!" << endl; w = new char; return w; 2.2 Dynamiczna alokacja tablicy. Dzi ki operatorowi new mo»na równie» tworzy tablice: int *wsk; wsk = new int [rozmiar]; Dzi ki powy»szym dwóm linijkom stworzyli±my tablic o rozmiarze rozmiar (warto± wyra»ona liczb caªkowit ). Dziaªanie operatora new tworzy w pami ci miejsce dla bezimiennej tablicy i przypisuje jej zerowy adres wska¹nikowi wsk. Taka tablica dziaªa na identycznych zasadach jak obiekty tworzone w poprzednim podpunkcie. float srednia (float a, float tab[ ]); 7
int rozm=0, i; float *wsk, wynik; cout << "Program obliczy srednia arytmetyczna kilku liczb\n"; cout << "Podaj ile liczb chcesz wprowadzic w celu obliczenia" << " sredniej: "; cin >> rozm; wsk = new float[rozm]; for (i = 0; i<rozm; i++) { cout << "Podaj wartosc dla " << i+1 << " elementu: "; cin >> wsk[i]; cout << "Policze srednia dla nastepujacych liczb: "; for (i=0; i<rozm; i++) { cout << wsk[i] << ", "; wynik = srednia(rozm, wsk); cout << "Srednia powyzszych liczb wynosi: " << wynik << endl; delete wsk; wsk = NULL; float srednia (float a, float tab[ ]) { int b; float sred; for (b =0; b<a; b++) { sred += tab[b]; sred /= a; return sred; Czasem napisany program mo»e zu»ywa zbyt wiele pami ci i niestety nie uda si stworzy nowego obiektu. Zamiast tego przypisana do niego zostanie warto± NULL (0). Je±li planujemy w programie kreowa wiele obiektów dynamicznych istnieje sposób by zwyczajnie sprawdza, czy dany obiekt zostaª stworzony. Oto przykªad p tli sprawdzaj cej: int rozm; float *wsk; wsk = new float[rozm]; 8
if (!wsk) { error("pamiec sie wyczerpala"); Zadanie W oparciu o zagadnienia wymienione w niniejszej instrukcji prosz udoskonali prosty interface u»ytkowy automatu do sprzeda»y przek sek i napoi stworzony na poprzednich zaj ciach. Wymogi dotycz ce programu: 1. Program ma ÿprzyjmowa " opªaty za wybrane przek ski i ÿwydawa " reszt 2. Pula pieni»na w automacie powinna by z góry ustalona podczas pierwszego uruchomienia programu (np. 50PLN). Pula powinna by rozdzielona na konkretne ilo±ci nominaªów monet (5,2,1,0.5,0.2,0.1 PLN). Wszelkie modykacje w puli pieni»nej powinny by eksportowane do zewn trznego pliku po ka»dej transakcji. 3. Podczas wydawania reszty automat powinien ustala priorytet wydawania monet, których ma w nadmiarze. 4. Podczas dokonywania opªaty przez u»ytkownika, musi on wprowadzi jakimi nominaªami pªaci. Je±li automat nie b dzie w stanie wyda reszty, ma wy±wietli si komunikat z pro±b o zapªat odliczon gotówk. 5. Wszelkie modykacje ilo±ci bilonu w automacie maj by przeprowadzane z wykorzystaniem wska¹ników. 6. Program powinien by wyposa»ony w ukryt funkcj uruchamian odpowiednim kodem, dzi ki której mo»liwa b dzie edycja dost pnej w automacie puli pieni»nej. 9