to get the file
Transkrypt
to get the file
Instrukcja Podstawy programowania 2 laboratoryjna 1 Temat: Wprowadzenie do wskaźników Przygotował: mgr inż. Tomasz Michno 1 Wstęp teoretyczny Rysunek przedstawia najważniejszą różnicę pomiędzy zmiennymi, a wskaźnikami: zmienna przechowuje dane (bezpośrednio wskazuje na komórkę w pamięci z danymi), natomiast wskaźnik przechowuje adres w pamięci, za pomocą którego można uzyskać dostęp do danych. To, jak zostaną rozpoznane dane znajdujące się pod tym adresem, zależy od zadeklarowanego typu wskaźnika (istnieje możliwość zmiany rozpoznawanego typu za pomocą rzutowania, jednak nie będziemy się tym na razie zajmowali). 1.1 Deklarowanie wskaźników Wskaźniki deklaruje się bardzo podobnie jak zwykłe zmienne - w celu utworzenia wskaźnika wystarczy poprzedzić typ znakiem „^”: nazwaWskaźnika : ^typ; W celu uzyskania dostępu do wartości, która znajduje się pod wskazywanym przez wskaźnik adresem, należy użyć znaku „^” za wskaźnikiem. Dostęp taki pozwala zarówno na odczyt, jak i modyfikację. Przykładowo, wskaźnik typu integer można zadeklarować następująco: wskaznik : ^integer; Natomiast odczytanie wartości i wyświetlenie jej na ekranie może zostać napisane następująco: writeln( 'Wartosc odczytana przez wskaznik: ', wskaznik^ ); 1.2 Wskaźniki i zmienne Wskaźniki mogą wskazywać na zmienne deklarowane standardowo (w sekcji var) oraz na zmienne dynamiczne (tworzone w trakcie działania programu), z którymi najczęściej są wykorzystywane. Aby powiązać wskaźnik ze „zwykłą” zmienną, wystarczy przypisać do niego adres tej zmiennej. Adres zmiennej można uzyskać poprzedzając zmienną znakiem @. przykład: var liczba : integer; wskaznik : ^integer; BEGIN {...} wskaznik := @liczba; writeln('Liczba odczytana przez wskaznik: ', wskaznik^ ); {...} END. Początkowo taki sposób dostępu do danych może wydawać się niepotrzebnym utrudnieniem, jednak jak się zaraz okaże może być przydatny. Przykład 1. Wskaźnik może być przydatny, gdy chcemy zdecydować podczas działania programu, którą zmienną przetwarzać bez niepotrzebnego kopiowania tego samego kodu dla każdej zmiennej. Alternatywnym rozwiązaniem jest użycie dodatkowej zmiennej (która przechowa wartość do przetworzenia), jednak nie jest zalecane ze względu na większe zużycie pamięci i konieczność ponownego przypisania wartości do właściwej zmiennej. Kod z użyciem wskaźnika program przyklad1; uses CRT; var a,b,c : integer; wybor : char; wskaznik : ^integer; BEGIN write('Podaj wybor (a, b lub c): '); readln(wybor); a:=1; b:=2; c:=3; wskaznik:=@a; case wybor of 'a': wskaznik:=@a; 'b': wskaznik:=@b; 'c': wskaznik:=@c; end; Kod bez użycia wskaźników program przyklad1; uses CRT; var a,b,c : integer; wybor : char; BEGIN write('Podaj wybor (a, b lub c): '); readln(wybor); a:=1; b:=2; c:=3; case wybor of 'a': begin writeln('wybrano liczbe: ', a); a:=2-a; a:=sqr(a); writeln('liczba po przeksztalceniach: ', a); end; 'b': begin writeln('wybrano liczbe: ', b); b:=2-b; b:=sqr(b); writeln('liczba po przeksztalceniach: ', b); end; 'c': begin writeln('wybrano liczbe: ', c); c:=2-c; c:=sqr(c); writeln('liczba po przeksztalceniach: ', c); end; end; writeln('wybrano liczbe: ', wskaznik^); wskaznik^:=2-wskaznik^; wskaznik^:=sqr(wskaznik^); writeln('liczba po przeksztalceniach: ', wskaznik^); readln; END. readln; END. Przykład 2. Przekazywanie przez parametr w procedurach i funkcjach – odpowiednik słowa kluczowego var (w większości języków jest to jedyny sposób na stworzenie odpowiednika słowa var). program przyklad2; uses CRT; type Pinteger = ^integer; var liczba : integer; procedure dodajDziesiec( wskLiczba : Pinteger ); begin wskLiczba^:= wskLiczba^ + 10; end; BEGIN liczba:=2; writeln('Liczba = ', liczba); dodajDziesiec(@liczba); writeln('Liczba = ', liczba); readkey; END. 1.3 Wskaźniki i zmienne dynamiczne. Zmienne dynamiczne są zmiennymi tworzonymi w trakcie działania programu, w dowolnym momencie jego działania. Ich utworzenie zazwyczaj jest związane z akcją użytkownika lub określonym stanem programu, co powoduje że trudno jest użyć zwykłych zmiennych. Często są związane również z dużymi danymi, które nie mogą zostać utworzone w Pascalu w zwykły sposób (właśnie z powodu rozmiaru) lub gdy niezbędna jest większa kontrola nad zużyciem pamięci. Utworzenie zmiennej dynamicznej odbywa się za pomocą procedury New(wskaźnik), która przydziela aplikacji odpowiedni fragment pamięci, a następnie zapisuje jej adres w podanym jako parametr wskaźniku. Wielkość przydzielonej pamięci zależy od typu wskaźnika. Bardzo ważne jest, aby później taki fragment pamięci zwolnić za pomocą procedury Dispose(wskaźnik), ponieważ w przeciwnym wypadku może dojść do tzw. wycieku pamięci1. Wycieki pamięci polegają na niezwalnianiu całej przydzielonej pamięci, co przy dużej liczbie alokacji może spowodować całkowite zapełnienie pamięci dostępnej dla programu. W celu wykrywania takich błędów przydatna jest zdefiniowana w Pascalu stała o nazwie MemAvail, wyświetlająca ilość wolnej pamięci. Przykład: Program przyklad3; uses CRT; var wskaznik : ^integer; BEGIN clrscr; writeln('Wolna pamiec: ', MemAvail, ' bajtow.'); New(wskaznik); writeln('Wolna pamiec: ', MemAvail, ' bajtow.'); Dispose(wskaznik); writeln('Wolna pamiec: ', MemAvail, ' bajtow.'); readkey; END. {wyczyszczenie ekranu} {wolna pamięć przed alokacją} {alokacja pamięci} {wolna pamięć po alokacji} {zwolnienie przydzielonej pamięci} {wolna pamięć pod koniec programu} Wynik działania: Jak widać powyżej, ilość wolnej pamięci zmniejszyła się po utworzeniu zmiennej dynamicznej, natomiast po jej usunięciu wróciła do poprzedniej wartości. 2 Zadania Zadanie 1. Wypisz liczbę wolnej pamięci (w bajtach). Zadanie 2. Napisz program, w którym za pomocą wskaźnika zmienisz wartość zmiennej (tego samego typu). Wyświetl wyniki na każdym etapie działania programu. 1 Więcej pod adresem: http://pl.wikipedia.org/wiki/Wyciek_pami%C4%99ci Zadanie 3. Napisz program, w którym: a) • utworzysz zmienną typu Real (nazwijmy ją zmiennaReal) i przypiszesz jej dowolną wartość rzeczywistą, np.: zmiennaReal:=1.5; • utworzysz wskaźnik typu integer (wsk), który wskazuje na zmiennaReal • wyświetlisz zawartość zmiennaReal • wyświetlisz zawartość wsk^ • do wsk przypiszesz dowolną liczbę integer, np.: wsk^:=10; • wyświetlisz zawartość zmiennaReal • wyświetlisz zawartość wsk^ • co można było zauważyć? • utworzysz zmienną o nazwie znak typu char i przypiszesz do niej dowolną literę (lub inny b) znak drukowalny) • utworzysz wskaźnik o nazwie wskZnak typu byte, który wskazuje na znak • wyświetlisz zawartość znak oraz zawartość wskZnak • do wskZnak przypiszesz dowolną liczbę z przedziału od 33 do 126 • wyświetlisz zawartość znak oraz zawartość wskZnak • co można było zauważyć? Zadanie 4. Napisz program, który utworzy dwie tablice, każda typu integer indeksowana od 1 do 30000 (rozmiar 60000 bajtów). Spróbuj najpierw utworzyć je jako zwykłe zmienne, a następnie jako zmienne dynamiczne. Wskazówka: zadeklaruj typ tablicowy. Zadanie 5. Napisz procedurę, która będzie dynamicznie tworzyła rekord wizytówki (imię, nazwisko, telefon, email) z wartości podanych jako parametry. Równocześnie procedura ma zwracać poprzez parametr wskaźnik na nowo utworzony rekord (bez wykorzystania słowa var w parametrze).