semestr drugi Wyk ad drugi ł 1. Wska niki (przypomnienie) ź

Transkrypt

semestr drugi Wyk ad drugi ł 1. Wska niki (przypomnienie) ź
Podstawy Programowania – semestr drugi
Wykład drugi
1.
Wskaźniki (przypomnienie)
Zmienna wskaźnikowa jest zmienną, która nie przechowuje bezpośrednio danej, lecz adres komórki pamięci (lub pierwszej komórki z grupy komórek), w której zawarta
jest dana. Adres ten nazywany jest również wskaźnikiem, dowiązaniem lub odniesieniem. Często również o zmiennej wskaźnikowej mówi się w skrócie „wskaźnik”.
W języku Pascal typ zmiennej wskaźnikowej możemy określić w następujący sposób:
type wskaznik = ^typ_zmiennej;
gdzie typ_zmiennej może określać zarówno prosty typ danych, jak i takie typy jak np.: tablica i rekord. Istnieje również typ zmiennej wskaźnikowej, który nie określa
typu wartości na którą zmienna wskazuje – pointer. Mając zdefiniowany typ możemy utworzyć zmienną typu wskaźnikowego w następujący sposób:
var wsk:wskaznik;
Możemy to również uczynić w inny sposób (w praktyce może się on okazać mniej użyteczny niż ten zaprezentowany powyżej):
var wsk:^typ_zmiennej;
Wartością początkową globalnej zmiennej wskaźnikowej jest specjalna wartość oznaczająca, że zmienna ta nie wskazuje na nic. W języku Pascal nosi ona nazwę NIL.
Zanim będziemy mogli skorzystać ze zmiennej wskaźnikowej musimy przydzielić jej obszar pamięci do której będziemy zapisywać dane. Procedury przydzielające
i zwalniające pamięć stosownie do naszych potrzeb będą opisane w dalszej części wykładu. Możemy też sprawić, aby zmienna wskaźnikowa wskazywała na obszar
pamięci, który zajmuje „zwykła” zmienna:
1
1 program wskazniki;
2 uses crt;
3 var
4
s:byte;
5
w:^byte;
6 begin
7 clrscr;
8 s:=4;
9 w:=@s;
10 writeln(s);
Operator „@” (wymawiamy jako „et”) odczytuje adres zmiennej globalnej „s” i zapisuje go w zmiennej wskaźnikowej „w”,
2
która ma wskazywać na wartość typu byte (wiersz 9 ), czyli takiego samego typu, jak wartość przechowywana
w zmiennej „s”. Po odczytaniu tego na co wskazuje „w” i co jest zapisane w zmiennej „s” okazuje się, że jest to ta sama
wartość. Co więcej, wystarczy zmodyfikować tę wartość dla którejkolwiek zmiennej, aby zmieniła się również dla
drugiej. Dzieje się tak, ponieważ zmienna „w” wskazuje na obszar pamięci, z którym związana jest zmienna „s”. Zanim
objaśnimy bliżej to zjawisko warto zwrócić uwagę na to w jaki sposób posługujemy się zmienną wskaźnikową. Jeśli
chcemy zapisać jakąś wartość do obszaru pamięci wskazywanego przez zmienną wskaźnikową to robimy to tak, jak ma
to miejsce w wierszu 14 programu. W podobny sposób dokonujemy odczytu (wiersz 11). Jeśli użylibyśmy zapisu np.:
w:=3 otrzymalibyśmy błąd czasu kompilacji. Ten zapis oznaczałby, że chcemy, aby zmienna „w” wskazywała na komórkę
o adresie „3”, a ponieważ nie jest to prawidłowe określenie adresu, kompilator zaprotestuje. Aby lepiej wyobrazić sobie
działanie wskaźników możemy posłużyć się bardzo uproszczonym modelem pamięci. Pamięć będzie tablicą elementów
typu byte. Elementy te nazywamy komórkami (w rzeczywistości komórki też mają rozmiar 1 bajta). Indeksy elementów
będziemy traktować jak adresy komórek. Załóżmy, że w mamy dwie zmienne, takie same, jak w programie obok.
3
Zmienne „s” i „w” są wiązane na etapie kompilacji z pewnymi komórkami w pamięci , jednak zmienna „w” potrafi
przechowywać adresy innych komórek. Po wykonaniu dziewiątego wiersza programu obraz pamięci prezentuje się
następująco:
11 writeln(w^);
zawartość
12 s:=5;
w
1
2
13 writeln(w^);
s
2
4
14 w^:=3;
3
15 writeln(s);
4
16 readln;
17 end.
2.
adres
Zmienna „w” zawiera adres komórki pamięci (2) z którą związana jest zmienna „s”. Zaprezentowany w programie obok
sposób posługiwania się zmiennymi wskaźnikowymi w niektórych zagadnieniach jest bardzo przydatny, ale zmienne te
są daleko bardziej elastyczne.
Struktury dynamiczne
W poprzednim paragrafie wspomniano o możliwości przydzielania obszaru pamięci, w którym będą przechowywane informacje. Te obszary będziemy nazywać
zmiennymi dynamicznymi (lub zmiennymi wskazywanymi, choć jest to pojęcie trochę szersze). Możemy przydzielić miejsce w pamięci nie tylko na pojedynczą zmienną,
ale również na całą strukturę. To rozwiązanie posiada dwie zalety. Rozmiar struktury dynamicznej nie jest z góry określony tak, jak ma to miejsce w przypadku
struktur globalnych lub lokalnych. Pamięć na struktury dynamiczne możemy przydzielać (alokować) i zwalniać (dealokować) podczas działania programu, w zależności
od potrzeb. Proces ten nie dzieje się automatycznie, jak w przypadku zmiennych lokalnych (zwanych czasem z tego powodu automatycznymi), ale programista ma na
niego wpływ, co umożliwia mu lepsze zarządzanie pamięcią. Miejsce dla zmiennych lub struktur dynamicznych jest przydzielane w obszarze pamięci programu, który
4
nazywa się stertą . Istnieje kilka procedur przydzielających i zwalniających pamięć. Najprostszymi są new i dispose. Procedura new przydziela tyle miejsca na stercie ile
wynika z typu zmiennej wskaźnikowej do której zapisywany jest adres tej pamięci. Pamięć przydzieloną przez new zwalniamy przy pomocy dispose. Jeśli chcemy
przydzielić ściśle określoną ilość pamięci możemy posłużyć się procedurą getmem. Do zwolnienia tej pamięci używamy procedury freemem. Procedura mark zapamiętuje
bieżący wskaźnik sterty. Procedura release zwalnia obszar pamięci od miejsca na które wskazuje bieżący wskaźnik sterty do miejsca, którego adres został zapamiętany
przy pomocy procedury mark. Przykłady użycia tych procedur, jak i przykłady tworzenia prostych dynamicznych struktur danych zostały omówione na poprzednim
wykładzie, więc tutaj zostaną pominięte. Zmienne dynamiczne pozwalają jednak tworzyć bardziej zaawansowane struktury, które nazywamy strukturami
abstrakcyjnymi. Abstrakcyjny typ danej określa nie tylko zbiór wartości jakie może przechowywać zmienna tego typu, ale również operacje jakie możemy wykonywać
na tych wartościach. Najprostszym przykładem abstrakcyjnej struktury danych (również struktury dynamicznej) jest stos.
1
2
3
4
Zamiast tego operatora możemy użyć funkcji „addr”.
Numery wierszy nie są częścią kodu źródłowego!
Tak jest w uproszczonym modelu pamięci jaki przyjęliśmy. W rzeczywistości zmienna wskaźnikowa zajmuje 4 lub 8 komórek pamięci (4 lub 8 bajtów).
Turbo Pascal posiada dyrektywę $M, która pozwala regulować rozmiar stosu i sterty. W tym celu w nawiasach klamrowych umieszczamy nazwę dyrektywy ($M), a za
nią trzy liczby rozdzielone przecinkami. Pierwsza z nich określa rozmiar stosu, druga minimalny rozmiar sterty, a trzecia maksymalny rozmiar sterty.
1
Podstawy Programowania – semestr drugi
3.
Stos
5
Stos jest przypadkiem szczególnym listy . Elementy stosu, w których zapisane są dane powiązane są ze sobą tworząc listę. Operacje dodawania i usuwania dotyczą
tylko jednego końca tej listy. Działanie stosu określa się angielskim skrótem LIFO (ang. Last In First Out – ostatni nadszedł, pierwszy wychodzi). Pojedynczy element
stosu może mieć następującą strukturę:
Do zdefiniowania typu pojedynczego elementu stosu (nazywanego również typem
bazowym stosu) posługujemy się typem rekordowym. Pole dana może mieć
type
oczywiście inny typ, niż ten który został zaprezentowany. W zapisie obok możemy
dostrzec dwa ciekawe elementy składniowe. Typ wskaźnikowy na zmienną typu
„element” jest zdefiniowany wcześniej niż sam typ „element”. Rekord zawiera pole
(„wsk”), które jest wskaźnikiem na rekord tego samego typu co on. Z tego względu
stos nazwany jest też strukturą rekurencyjną. Mając określony typ elementu stosu
musimy jeszcze określić podprogramy, które na elementach tego typu będą
wykonywały operacje. Tych podprogramów może być kilka, jednak najważniejsze
z nich to „push” i „pop”.
wskaznik=^element;
element = record
dana:integer;
wsk:wskaznik;
end;
Procedura „push” umieszcza nowy element na stosie. Przez parametry pobiera
wskaźnik na element, który znajduje się na szczycie stosu („x”) oraz wartość, która
ma być zapisana w elemencie („y”). Należy zwrócić uwagę, że wskaźnik jest
przekazany przez zmienną, gdyż procedura będzie modyfikowała jego zawartość.
Procedura posiada również zmienną lokalną, która jest typu „wskaźnik na element
stosu”. W wierszu szóstym za pomocą procedury „new” zostaje przydzielona pamięć
na nowy element stosu, którego adres zostaje zapisany w zmiennej „top”. W wierszu
siódmym w tym elemencie zostaje zapisana informacja, którą ma przechowywać.
Teraz należy już tylko umieścić nowy element na szczycie stosu. Najpierw musimy
„połączyć” go z resztą stosu. Adres elementu, który jest bieżącym wierzchołkiem stosu
jest zapisany w parametrze „x”. Należy więc zawartość tej zmiennej przepisać do pola
„wsk” nowego elementu, co też czynimy w wierszu ósmym. Po wykonaniu instrukcji
6
zawartej w tym wierszu nowy element staje się pierwszym elementem stosu,
jednakże parametr „x” nie zawiera jeszcze jego adresu, lecz adres elementu
poprzedniego. Należy więc zmodyfikować zawartość zmiennej „x” i zapisać w niej
adres nowego wierzchołka stosu, co zrealizowane jest w wierszu dziewiątym
7
procedury „push”. Jej działanie można również zobrazować rysunkiem :
1 procedure push(var x:wskaznik; y:integer);
2 {Odkłada element na stos.}
3 var
4 top:wskaznik;
5 begin
6 new(top);
7 top^.dana:=y;
8 top^.wsk:=x;
9 x:=top;
10 end;
1. Przed wykonaniem wiersza ósmego procedury „push”
2. Po wykonaniu wiersza ósmego
3. Po wykonaniu wiersza dziewiątego
top
dana
wsk
top
dana
wsk
top
dana
wsk
x
dana
wsk
x
dana
wsk
x
dana
wsk
dana
NIL
dana
NIL
dana
NIL
Wartości jakie znajdą się w polach „dana” i „wsk” elementu mogą być różne, w zależności od tego do czego użyjemy stosu i jak zostanie on stworzony na stercie.
Wyjątkiem jest tylko pole „wsk” ostatniego elementu stosu. Musi ono mieć wartość „NIL”, oznaczającą że pole to nie wskazuje na żaden inny element, a element je
8
zawierający jest ostatnim elementem stosu. Należy zastanowić się, czy procedura „push” zadziała prawidłowo, kiedy będziemy tworzyć taki element. Wartość pola
„wsk” elementów jest ustalana w wierszu ósmym procedury. Jeśli adres wierzchołka stosu będziemy przechowywać w zmiennej globalnej, którą będziemy następnie
przekazywać do procedury „push”, to w polu „wsk” ostatniego elementu zostanie zapisana prawidłowa wartość, gdyż globalna zmienna wskaźnikowa domyślnie jest
inicjalizowana wartością „NIL”. W innych przypadkach należy zadbać o odpowiednią inicjalizację zmiennej przechowującej adres wierzchołka przed wywołaniem po raz
pierwszy w programie procedury „push”. Wyjaśnienia wymaga również kwestia zmiennej „top”. Ponieważ jest ona zmienną lokalną, więc po zakończeniu procedury jest
automatycznie niszczona i nie będzie wskazywać na żaden z elementów stosu. Trzeba również zwrócić uwagę, że procedura „push” nie posługuje się zmienną
lokalną wskaźnikową (w tym wypadku „top”), dopóki nie przydzieli na pomocą procedury „new” obszaru pamięci na który ta zmienna będzie
wskazywać. To spostrzeżenie jest prawdziwe nie tylko dla stosu, ale również dla innych struktur dynamicznych. Posługiwanie się niezainicjalizowaną
lokalną zmienną wskaźnikową jest szczególnie niebezpieczne, ponieważ zmienne tego rodzaju nie są domyślnie inicjalizowane wartością „NIL” i mogą zawierać adres
dowolnego miejsca w pamięci operacyjnej.
5
6
7
8
Na tym wykładzie będzie to lista liniowa. Oba pojęcia (listy i listy liniowej) zostaną wyjaśnione na przyszłych wykładach.
To znaczy: znajdującym się na szczycie stosu.
Rysunek pokazuje w jaki sposób obrazujemy powiązania między poszczególnymi elementami struktur dynamicznych. Prostokąty obrazują zmienne wskaźnikowe
i elementy stosu. Strzałki określają, że zmienna wskaźnikowa, lub pole od którego strzałka „wychodzi” przechowuje adres elementu do którego strzałka „dochodzi”.
To znaczy: znajduje się na dnie stosu.
2
Podstawy Programowania – semestr drugi
Procedura „pop" ma działanie odwrotne do procedury „push” - usuwa element, który
znajduje się na szczycie stosu. Posiada ona dwa parametry, pierwszy jest
wskaźnikiem na pierwszy element stosu („x”), a poprzez drugi zostanie zwrócona
wartość, która jest zapisana w tym elemencie („y”). Obydwa parametry są
przekazywane przez zmienną. Procedura ta posiada również jedną zmienną lokalną,
w której będzie zapamiętywany wskaźnik do elementu znajdującego się za pierwszym
elementem na stosie. W wierszu szóstym procedury parametrowi „y” zostaje
przypisana wartość, która umieszczona jest w polu „dana” elementu ze szczytu stosu.
Teraz należy ten element zdjąć ze stosu. Najpierw w wierszu siódmym, w zmiennej
„bottom” zapamiętywana jest zawartość pola „wsk” bieżącego wierzchołka stosu (jest
to adres elementu znajdującego się „pod” wierzchołkiem), następnie, przy pomocy
procedury „dispose” zwalniana jest pamięć przydzielona na ten element. Innymi
słowy jest on usuwany. Ostatnią czynnością jaka jest wykonywana w procedurze
„pop” jest modyfikacja zawartości wskaźnika „x”, tak aby wskazywał na element,
który obecnie znajduje się na szczycie stosu. Jego adres został zapamiętany
w zmiennej „bottom”, wystarczy więc przepisać zawartość tej zmiennej do parametru
„x” (wiersz 9). Działanie tej procedury można przedstawić za pomocą następującego
rysunku:
1 procedure pop(var x:wskaznik; var y:integer);
2 {Pobiera element ze stosu.}
3 var
4 bottom:wskaznik;
5 begin
6 y:=x^.dana;
7 bottom:=x^.wsk;
8 dispose(x);
9 x:=bottom;
10 end;
1. Po wykonaniu wiersza siódmego
x
dana
wsk
bottom
dana
wsk
dana
NIL
2. Po wykonaniu wiersza ósmego
x
bottom
dana
wsk
3. Po wykonaniu wiersza dziewiątego
x
dana
wsk
bottom
dana
NIL
dana
wsk
dana
NIL
Procedura „dispose” oznacza jedynie pamięć w której jest zapisany element jako zwolnioną, natomiast nie niszczy zawartości tej pamięci, ani nie zmienia wartości
przechowywanej w zmiennej wskaźnikowej (wbrew temu, co pokazuje rysunek). Mimo, że po wywołaniu „dispose” możliwe jest „sięgnięcie” do zwolnionego
elementu, nigdy nie należy tego robić ! Taka operacja może w przypadku innych języków programowania i bardziej zaawansowanych aplikacji okazać się bardzo
niebezpieczna i co gorsza trudna do wykrycia. Zmienna „bottom”, podobnie jak zmienna „top” w procedurze „push” jest automatycznie niszczona po zakończeniu
wykonania procedury. Aby procedura działała poprawnie należy zadbać, aby parametr „x” nie miał wartości początkowej równej „NIL”. Taka wartość wskaźnika
oznaczałaby oczywiście, że nie ma żadnego elementu na stosie, ale również spowodowałaby błąd wykonania w szóstym wierszu procedury.
4.
Zastosowania stosu
Część pamięci operacyjnej, która należy do programu, przeznaczona jest na stos sprzętowy. W tej pamięci zapamiętywane są, po każdym uruchomieniu podprogramu
(procedury lub funkcji), ramki stosu, inaczej zwane rekordami aktywacyjnymi, które zawierają parametry wywołania podprogramu, jego zmienne lokalne oraz adres
powrotu do programu. Pozwala to na wywoływanie podprogramów z wnętrza innych podprogramów, a także na rekurencyjne wywołania podprogramu. W tym
przypadku stos jest jednak obsługiwany sprzętowo i programista nie musi go tworzyć samemu. Stos jest również używany przez translatory języków programowania
(kompilatory i interpretery). W edytorach tekstu struktura stosu może zostać użyta do realizacji wielopoziomowej operacji „cofnij” (ang. undo). W tym ostatnim
przypadku na stosie zapamiętywana jest pewna liczba ostatnio wykonanych przez użytkownika operacji, takich jak: wprowadzanie znaków, zmiana czcionki, itd.
5.
Przykłady
Poniżej zaprezentowano trzy programy, które pokazują proste zastosowania stosu. Pierwszy z nich zapisuje liczby, które wprowadzi użytkownik na stos, a następnie
odczytuje je ze stosu. Zgodnie z zasadą LIFO zostaną one wypisane na ekranie w odwrotnej kolejności niż zostały umieszczone na stosie.
1 program stos;
2 uses crt;
3 type
4
wskaznik=^element;
5
element = record
6
dana:integer;
7
wsk:wskaznik;
8
end;
9 var
10
p:wskaznik;
11
nu,ne:integer;
3
Podstawy Programowania – semestr drugi
W bloku głównym programu, w wierszach 35, 46, 52 wypisywana jest
na ekran wielkość wolnej pamięci. Pozwala to ustalić, czy pamięć
poprawnie została przydzielona, a następnie zwolniona. Pętla
w wierszach 48 – 51 kończy się w momencie, kiedy wskaźnik na
element znajdujący się na szczycie stosu będzie miał wartość „NIL”,
stąd wymóg, aby pole „wsk” ostatniego elementu na stosie miało
właśnie tę wartość.
12
13 procedure push(var x:wskaznik; y:integer);
14 var
15 top:wskaznik;
16 begin
17 new(top);
18 top^.dana:=y;
19 top^.wsk:=x;
20 x:=top;
21 end;
22
23 procedure pop(var x:wskaznik; var y:integer);
24 var
25 bottom:wskaznik;
26 begin
27 y:=x^.dana;
28 bottom:=x^.wsk;
29 dispose(x);
30 x:=bottom;
31 end;
32
33 begin
34 clrscr;
35 writeln('Dostępna pamięć: ',MemAvail);
36 writeln('Ile elementów odłożyć na stos ?');
37 readln(ne);
38 while ne>0 do
39
begin
40
writeln('Podaj wartość elementu:');
41
readln(nu);
42
push(p,nu);
43
dec(ne);
44
end;
45 clrscr;
46 writeln('Dostępna pamięć: ',MemAvail);
47 writeln('Na stos odłożono następujące elementy: ');
48 while p<>nil do begin
49
pop(p,nu);
50
writeln(nu);
51 end;
52 writeln('Dostępna pamięć: ',MemAvail);
53 readln;
54 end.
4
Podstawy Programowania – semestr drugi
Program „onp” oblicza wartości prostych wyrażeń arytmetycznych zapisanych w Odwrotnej Notacji Polskiej. Liczby
użyte w tych wyrażeniach muszą składać się wyłącznie
z jednej cyfry. Przykładowymi wyrażeniami, których
wartości mogą być obliczone przez program są: 45+3*, czyli
w „zwykłej” notacji (4+5)*3 lub 45+23+* (czyli w notacji
wrostkowej (4+5)*(2+3)). Zasadniczą częścią programu jest
procedura „wyrazenie”. Czyta ona znak po znaku wyrażenie
wprowadzane przez użytkownika. Jeśli odczytany znak nie
jest znakiem działania, to konwertowany jest na liczbę,
która zapamiętywana jest na stosie (wiersz 64). Jeśli
zostanie odczytany znak działania, np. „+”, to ze stosu
zdejmowane są dwie liczby (wiersze 46, 47), wykonywane
jest dodawanie (wiersz 48) i na stosie zapamiętywany jest
jego wynik (wiersz 49). W momencie kiedy użytkownik
naciśnie klawisz „Enter” procedura wychodzi z pętli (wiersz
42), zdejmuje ze stosu element, w który zapamiętana jest
wartość wyrażenia i ją wypisuje na ekran.
1 program onp;
2 uses crt;
3 type
4
wskaznik=^element;
5
element = record
6
dana:integer;
7
wsk:wskaznik;
8
end;
9 var
10
p:wskaznik;
11
12 procedure push(var x:wskaznik; y:integer);
Zamieszczony obok program można napisać bez jawnego
użycia stosu lub implementując stos nie w oparciu o listę,
a w oparciu o tablicę. Można go również rozbudować, tak
aby akceptował liczby wielocyfrowe i bardziej skomplikowane wyrażenia.
13 {Odkłada element na stos.}
14 var
15 top:wskaznik;
16 begin
17 new(top);
18 top^.dana:=y;
19 top^.wsk:=x;
20 x:=top;
21 end;
22
23 procedure pop(var x:wskaznik; var y:integer);
24 {Pobiera element ze stosu.}
25 var
26 bottom:wskaznik;
27 begin
28 y:=x^.dana;
29 bottom:=x^.wsk;
30 dispose(x);
31 x:=bottom;
32 end;
33
34 procedure wyrazenie(var x:wskaznik);
35 var
36 a:char;
37 op1,op2,wyn:integer;
38 begin
39 writeln('Podaj wyrażenie w Odwrotnej Notacji Polskiej: ');
40 repeat
41
a:=readkey;
42
if a=#13 then break;
43
write(a);
44
case a of
45
'+': begin
5
Podstawy Programowania – semestr drugi
46
47
48
pop(x,op1);
pop(x,op2);
wyn:=op2+op1;
49
push(x,wyn);
50
51
end;
'-': begin
52
pop(x,op1);
53
pop(x,op2);
54
wyn:=op2-op1;
55
push(x,wyn);
56
57
end;
'*': begin
58
pop(x,op1);
59
pop(x,op2);
60
wyn:=op2*op1;
61
push(x,wyn);
62
63
end
else
64
65
push(x,ord(a)-48);
end;
66 until false;
67 writeln;
68 writeln('Wartość wyrażenia: ');
69 pop(x,wyn);
70 write(wyn);
71 end;
72 begin
73 clrscr;
74 wyrazenie(p);
75 readln;
76 end.
Program bin2dec zamienia podaną na wejście liczbę binarną na liczbę
dziesiętną. Procedura „pobierz” zapamiętuje kolejne cyfry liczby binarnej
na stosie do momentu, kiedy użytkownik naciśnie klawisz „Enter”.
Procedura „zwroc” zdejmuje kolejne cyfry tej liczby ze stosu, mnoży je przez
1 program bin2dec;
2 uses crt;
3
kolejne potęgi dwójki (zapamiętywane w zmiennej „b”), a otrzymane w ten
sposób wyniki cząstkowe dodaje do siebie i zapamiętuje w zmiennej „wyn”.
Po zdjęciu ze stosu i wykonaniu działań na ostatniej cyfrze wypisywany
jest na ekran wynik i procedura kończy swoje działanie.
4 type
5
wskaznik=^element;
6
element = record
7
dana:integer;
8
wsk:wskaznik;
9
Ten program można oczywiście napisać bez użycia stosu.
end;
10 var
11
p:wskaznik;
12
13 procedure push(var x:wskaznik; y:integer);
14 var
6
Podstawy Programowania – semestr drugi
15 top:wskaznik;
16 begin
17 new(top);
18 top^.dana:=y;
19 top^.wsk:=x;
20 x:=top;
21 end;
22
23 procedure pop(var x:wskaznik; var y:integer);
24 var
25 bottom:wskaznik;
26 begin
27 y:=x^.dana;
28 bottom:=x^.wsk;
29 dispose(x);
30 x:=bottom;
31 end;
32
33 procedure pobierz(var x:wskaznik);
34 var
35 a:char;
36 begin
37 clrscr;
38 writeln('Podaj liczbę binarną: ');
39 repeat
40
a:=readkey;
41
if a=#13 then break;
42
write(a);
43
push(x,ord(a)-48);
44 until false;
45 writeln;
46 end;
47
48 procedure zwroc(var x:wskaznik);
49 var
50 wyn,a,b:integer;
51 begin
52 wyn:=0;
53 b:=1;
54 while x<>nil do
55
begin
56
pop(x,a);
57
wyn:=wyn+b*a;
58
59
b:=b*2;
end;
7
Podstawy Programowania – semestr drugi
61
end;
62 writeln('Liczba zapisana w kodzie dziesiętnym: ');
63 write(wyn);
64 writeln;
65 end;
66
67 begin
68 pobierz(p);
69 zwroc(p);
70 readln;
71 end.
8