Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
РП_31_АСД.pdf
Скачиваний:
164
Добавлен:
23.02.2016
Размер:
2.92 Mб
Скачать
current
current
current

Тема 14. СТЕКИ

Стек – це один з різновидів однозв’язного лінійного списку, доступ до елементів якого можливий лише через його початок, що називається вершиною стеку. Стек працює за принципом «останнім прийшов – першим вийшов», що позначається абревіатурою LIFO (від англ. Last In First Out), і має такі властивості:

Елементи додаються у вершину (голову) стеку;

Елементи видаляються з вершини (голови) стеку;

Покажчик в останньому елементі стеку дорівнює nil;

Неможливо вилучити елемент із середини стеку, не вилучивши всі елементи, що йдуть попереду.

У програмуванні стеки мають широке застосування. Наприклад, під час виклику підпрограми адреса повернення до неї зберігається у стеку. Стек використовується компілятором під час обчислення виразів, до нього записуються значення локальних змінних тощо.

Для роботи зі стеком достатньо мати покажчик head на його вершину та допоміжний

покажчик current на елемент стеку.

Алгоритм вставки елемента до стеку

1.Виділити пам'ять для нового елемента стеку: new(current) (рис.1а).

2.Ввести дані до нового елемента: readln(current^.data) (рис.1б).

3.Зв’язати допоміжний елемент із вершиною: current^.next:=head (рис.1в).

4.Встановити вершину стеку на новостворений елемент: head:= current (рис.1г).

? ?

a

data ?

б

data next=head

в

current

head data next data next data next=nil

г

Рис.1 Вставка елемента до стеку: виділення пам'яті (а); введення даних (б); зв’язування з вершиною (в); переміщення вершини.

Значення покажчика head на вершину порожнього стеку є nil. Тому для створення стеку слід виконати оператор head=nil та повторити щойно наведений алгоритм потрібну кількість разів.

Алгоритм видалення елемента із не порожнього стеку

1.Створити копію покажчика на вершину стеку: current:=head; (рис.2а).

2.Перемістити покажчик на вершину стеку на наступний елемент: head:=current^.next; (рис.2б).

3.Звільнити пам'ять із-під колишньої вершини стеку: Dispose(current) (рис.2в). Зрозуміло, що для очищення всього стеку слід повторювати кроки 1-3 доти, доки покажчик

head не дорівнюватиме nil.

42

current

data

next

data

 

next

data

next=nil

head

а

 

 

 

 

 

 

 

current

data

next

data

 

next

data

next=nil

 

 

 

head

б

 

 

 

current

?

?

data

 

next

data

next=nil

в

Рис.2. Видалення елемента із не порожнього списку: створення копії покажчика на вершину стеку (а); переміщення вершини (б); звільнення пам'яті (в)

Програмна реалізація вище наведених алгоритмів наступна: program Yrok6;

{Створення, виведення та очищення списку} {$APPTYPE CONSOLE}

uses

 

SysUtils;

 

type

 

ptr=^Item;

{тип покажчика на елемент стеку}

Item=record

{тип елементу стеку}

data:string;

{інформаційне поле елемента}

next:ptr;

{покажчик на наступний елемент}

end;

 

var

 

head: ptr;

{вершина стеку}

current: ptr;

{допоміжний покажчик}

str: string;

{значення, що додається до стеку}

i:integer;

{параметр циклу}

n:integer;

{кількість елементів, що додаються до стеку}

key:char;

{номер пункту меню програми}

{Додавання елемента до стеку}

procedure push(value:string); {параметр - значення що додається} begin

new(current);

{виділення пам'яті для елемента}

current^.data:=value;

{ініціалізація інформаційного поля}

current^.next:=head;

{зв'язування елементів стеку}

head

:=current; {встановлення нової вершини стеку}

end;

{Видалення елемента зі стеку}

procedure pop(value:string); {параметр - змінна, значення якої буде виведене}

begin

 

current:=head;

{збереження адреси вершини стеку}

43

value :=head^.data; {збереження значення, що виводиться}

writeln(value);

{Відображення значення, що виводиться}

head :=current^.next; {перенесення вершини стеку на другий елемент}

dispose(current);

{звільнення пам'яті}

 

end;

 

 

 

{}

 

 

 

begin

 

 

 

writeln('

STACK');

 

head:=nil;

 

{стек п орожній}

 

repeat

 

{виведення меню}

 

writeln('=========================');

 

writeln('1.add elements to stack');

 

writeln('2.output and delet e stack');

 

writeln('3.exit');

 

 

write('press key 1..3: ');

 

readln(key); {Вибір пункту меню}

 

case key of

 

 

 

'1': begin

{додавання елементів до стеку}

 

write('enter stack length '); readln(n);

 

for i:=1 to n do

Рис.3. Результат роботи програми

begin

 

write('Enter data item ',i,': '); readln(str);

Yrok6. Обробка стеку

push(str);

{виклік процедури додавання елемента до стеку}

end;

 

 

 

end;

 

 

 

'2': begin

{вивести та очистити стек}

 

while head<>nil do pop(str); {видалення елемента зі стеку} writeln;

writeln('stack is empty '); end;

end;

until key='3'; {продовженн я роботи програми до вибору 3-го пункту меню} end.

Результат роботи програ ми Yrok6 зображений на рис.3.

44

Тема 15. МАШИННЕ ПРЕДСТАВЛЕННЯ СТЕКА І РЕАЛІЗАЦІЯ ОПЕРАЦІЙ

При представленні стека в статичній пам'яті для нього виділяється пам'ять, як для вектора. У дескрипторі цього вектора крім звичайних для вектора параметрів повинен перебувати також покажчик стека - адреса вершини стека. Покажчик стека може вказувати або на перший вільний елемент стека, або на останній записаний в стек елемент. (Все одно, який з цих двох варіантів вибрати, важливо надалі суворо дотримуватися його при обробці стека.) Надалі вважатиметься, що покажчик стека адресує перший вільний елемент і стек росте в сторону збільшення адрес.

При занесенні елемента в стек він записується на місце, яке визначається покажчиком стека, потім покажчик модифікується таким чином, щоб він вказував на наступний вільний елемент (якщо покажчик вказує на останній записаний елемент, то спочатку модифікується вказівник, а потім проводиться запис елемента). Модифікація покажчика полягає в додаванні до нього або в відніманні від нього одиниці.

Операція виключення елемента полягає в модифікації покажчика стека (в напрямку, зворотному модифікації при включенні) і вибірці значення, на яке вказує покажчик стека. Після вибірки слот, у якому розміщувався вибраний елемент, вважається вільним.

Операція очищення стека зводиться до запису в покажчик стека початкового значення - адреси початку виділеної області пам'яті.

Визначення розміру стека зводиться до обчислення різниці покажчиків: покажчика стека і адреси початку області.

Програмний модуль, представлений в наступному прикладі, ілюструє операції над стеком, що розширюється в бік зменшення адрес. Покажчик стека завжди вказує на перший вільний елемент.

const SIZE = ...; type data = ...;

{ Стек } unit Stack; Interface

const SIZE=...; { граничний розмір стека }

type data = ...; { елементи можуть мати буд-який тип } Procedure StackInit;

Procedure StackClr;

Function StackPush(a : data) : boolean; Function StackPop(Var a : data) : boolean; Function StackSize : integer; Implementation

Var StA : array[1..SIZE] of data; { Стек - дані }

top : integer;

 

Procedure StackInit;

{** ініціалізація - на початок }

begin top:=SIZE; end;

{** очищення = ініціалізація }

Procedure StackClr;

 

begin top:=SIZE; end;

{ ** занесення елемента в стек } Function StackPush(a: data) : boolean;

begin

45

if top=0 then StackPush:=false

else begin { занесення, потім – збільшення покажчика } StA[top]:=a; top:=top-1; StackPush:=true;

end; end; { StackPush }

{ ** вибірка елемента зі стеку } Function StackPop(var a: data) : boolean;

begin

if top=SIZE then StackPop:=false

else begin { покажчик збільшується, потім - вибірка } top:=top+1; a:=StA[top]; StackPop:=true;

end; end; { StackPop }

Function StackSize : integer; {** визначення розміру } begin StackSize:=SIZE-top; end;

END.

Стеки в обчислювальних системах

Стек є надзвичайно зручною структурою даних для багатьох завдань обчислювальної техніки. Найбільш типовим з таких завдань є забезпечення вкладених викликів процедур.

Нехай є процедура A викликає процедуру B, а та в свою чергу - процедуру C. Коли виконання процедури A дійде до виклику B, процедура A припиняється і керування передається на вхідну точку процедури B. Коли B доходить до виклику C, призупиняється B і керування передається на процедуру C. Коли закінчується виконання процедури C, керування повинне бути повернуто в B, причому в точку, наступну за викликом C. При завершенні B керування повинне повертатися в A, в точку, наступну за викликом B. Правильну послідовність повернень легко забезпечити, якщо під час кожному виклику процедури записувати адресу повернення в стек. Так, коли процедура A викликає процедуру B, в стек заноситься адреса повернення в A; коли B викликає C, в стек заноситься адреса повернення в B. Коли C закінчується, адреса повернення вибирається з вершини стека - а це адреса повернення в B. Коли закінчується B, в вершині стека знаходиться адресу повернення в A, і повернення з B відбудеться в A.

В мікропроцесорах сімейства Intel, як і в більшості сучасних процесорних архітектур, підтримується апаратний стек. Апаратний стек розташований в ОЗП, покажчик стека міститься в парі спеціальних регістрів - SS: SP, доступних для програміста. Апаратний стек розширюється в бік зменшення адрес, покажчик його адресує перший вільний елемент. Виконання команд CALL і INT, а також апаратних переривань включає в себе запис в апаратний стек адреси повернення. Виконання команд RET і IRET включає в себе вибірку з апаратного стека адреси повернення та передачу керування за цією адресою. Пара команд - PUSH і POP - забезпечує використання апаратного стека для програмного вирішення інших завдань.

Системи програмування для блочно-орієнтованих мов (PASCAL, C і ін) використовують стек для розміщення в ньому локальних змінних процедур та інших програмних блоків. При кожній активізації процедури пам'ять для її локальних змінних виділяється в стеку; при завершенні процедури ця пам'ять звільняється. Оскільки при викликах процедур завжди суворо дотримується вкладеність, то в вершині стека завжди знаходиться пам'ять, яка містить локальні змінні активною в даний момент процедури.

46