Instrukcja laboratoryjna 1 Podstawy programowania 2 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:
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 ) 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: liczba : integer; {...} wskaznik := @liczba; writeln('liczba odczytana przez wskaznik: ', wskaznik^ ); {...} 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. program przyklad1; a,b,c : integer; wybor : char; Kod z użyciem wskaźnika 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; program przyklad1; a,b,c : integer; wybor : char; Kod bez użycia wskaźników write('podaj wybor (a, b lub c): '); readln(wybor); a:=1; b:=2; c:=3; case wybor of 'a': writeln('wybrano liczbe: ', a); a:=2-a; a:=sqr(a); writeln('liczba po przeksztalceniach: ', a);
writeln('wybrano liczbe: ', wskaznik^); wskaznik^:=2-wskaznik^; wskaznik^:=sqr(wskaznik^); writeln('liczba po przeksztalceniach: ', wskaznik^); readln; 'b': writeln('wybrano liczbe: ', b); b:=2-b; b:=sqr(b); writeln('liczba po przeksztalceniach: ', b); 'c': writeln('wybrano liczbe: ', c); c:=2-c; c:=sqr(c); writeln('liczba po przeksztalceniach: ', c); readln; Przykład 2. Przekazywanie przez parametr w procedurach i funkcjach odpowiednik słowa kluczowego (w większości języków jest to jedyny sposób na stworzenie odpowiednika słowa ). program przyklad2; type Pinteger = ^integer; liczba : integer; procedure dodajdziesiec( wskliczba : Pinteger ); wskliczba^:= wskliczba^ + 10; liczba:=2; writeln('liczba = ', liczba); dodajdziesiec(@liczba); writeln('liczba = ', liczba); readkey; 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ęci 1. 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; clrscr; New(wskaznik); Dispose(wskaznik); readkey; Wynik działania: {wyczyszczenie ekranu} {wolna pamięć przed alokacją} {alokacja pamięci} {wolna pamięć po alokacji} {zwolnienie przydzielonej pamięci} {wolna pamięć pod koniec programu} 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ć? b) utworzysz zmienną o nazwie znak typu char i przypiszesz do niej dowolną literę (lub inny 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, e- mail) z wartości podanych jako parametry. Równocześnie procedura ma zwracać poprzez parametr wskaźnik na nowo utworzony rekord (bez wykorzystania słowa w parametrze).