Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Основы программирования. Борисенко

.pdf
Скачиваний:
1620
Добавлен:
09.04.2015
Размер:
9.31 Mб
Скачать

252 4 4 Простейшие структуры данных Стек Очередь

необходимости перерисовки области окна, о выборе пункта меню и т.п. Каждая программа имеет очередь запросов. Когда программа по¬ лучает свой квант времени на выполнение, она выбирает очередной запрос из начала очереди и выполняет его. Таким образом, работа оконного приложения состоит, упрощенно говоря, в последователь¬ ном выполнении запросов из ее очереди. Очередь поддерживается операционной системой.

Подход к программированию, состоящий не в прямом вызове про¬ цедур, а в посылке сообщений, которые ставятся в очередь запро¬ сов, имеет много преимуществ и является одной из черт объектноориентированного программирования. Так, например, если оконной программе необходимо завершить работу по какой-либо причине, лучше не вызывать сразу команду завершения, которая опасна, пото¬ му что нарушает логику работы и может привести к потере данных. Вместо этого программа посылает самой себе сообщение о необхо¬ димости завершения работы, которое будет поставлено в очередь за¬ просов и выполнено после запросов, поступивших ранее.

Р е а л и з а ц ия очереди на базе массива

Как у ж е было сказано, программисту массив дан свыше, все остальные структуры данных нужно реализовывать на его основе. Конечно, такая реализация может быть многоэтапной, и не всегда массив выступает в качестве непосредственной базы реализации. В случае очереди наиболее популярны две реализации: непрерывная на базе массива, которую называют также реализацией на базе кольце¬ вого буфера, и ссылочная реализация, или реализация на базе спис¬ ка. Ссылочные реализации будут рассмотрены ниже.

При непрерывной реализации очереди в качестве базы выступает массив фиксированной длины N , таким образом, очередь ограниче­ на и не может содержать более N элементов. Индексы элементов массива изменяются в пределах от 0 до N — 1. Кроме массива, ре¬ ализация очереди хранит три простые переменные: индекс начала очереди, индекс конца очереди, число элементов очереди. Элементы очереди содержатся в отрезке массива от индекса начала до индекса конца.

4.4.2. Стек

 

 

 

255

A . В процессе

ее выполнения возникает необходимость

выполнить

задачу B . Состояние задачи A запоминается, и компьютер

переходит

к выполнению

задачи B . Но ведь и при выполнении

задачи B

ком­

пьютер может

переключиться на другую задачу C ,

и нужно

будет

сохранить состояние задачи B , прежде чем перейти к C . Позже, по окончании C будет сперва восстановлено состояние задачи B , затем, по окончании B , — состояние задачи A . Таким образом, восстановле¬ ние происходит в порядке, обратном сохранению, что соответствует дисциплине работы стека.

Стек позволяет организовать рекурсию, т.е. обращение подпро¬

граммы к самой себе либо непосредственно, либо

через

цепочку

других вызовов. Пусть, например, подпрограмма A

выполняет ал¬

горитм, зависящий от входного параметра X и, возможно,

от состо­

яния глобальных данных. Д л я самых простых значений

X

алгоритм

реализуется непосредственно. В случае более сложных

значений X

алгоритм реализуется как сведение к применению того ж е алгоритма для более простых значений X . При этом подпрограмма A обра¬ щается сама к себе, передавая в качестве параметра более простое

значение X . При таком обращении предыдущее значение параметра

X ,

а также все локальные переменные подпрограммы A сохраняются

в

стеке. Д а л е е создается новый набор локальных переменных и пе¬

ременная, содержащая новое (более простое) значение параметра X . Вызванная подпрограмма A работает с новым набором переменных, не разрушая предыдущего набора. По окончании вызова старый на¬

бор локальных переменных

и старое состояние

входного параметра

X восстанавливаются из стека, и подпрограмма

продолжает работу

с того места, где она была

прервана.

 

На самом деле даже не приходится специальным образом сохра¬ нять значения локальных переменных подпрограммы в стеке. Дело в том, что локальные переменные подпрограммы (т.е. ее внутренние, рабочие переменные, которые создаются в начале ее выполнения и уничтожаются в конце) размещаются в стеке, реализованном аппаратно на базе обычной оперативной памяти. В самом начале работы подпрограмма захватывает место в стеке под свои локальные пере¬

менные, этот участок памяти

в аппаратном стеке называют обычно

блок локальных переменных

или по-английски frame ("кадр"). В мо¬

мент окончания работы подпрограмма освобождает память, удаляя из

256

4.4. Простейшие структуры данных. Стек. Очередь

стека блок своих локальных переменных.

 

 

Кроме локальных переменных, в аппаратном

стеке сохраняются

адреса возврата при вызовах подпрограмм. Пусть

в некоторой

точке

программы A вызывается подпрограмма B . Перед вызовом подпро¬

граммы B адрес инструкции, следующей за инструкцией вызова B ,

сохраняется

в стеке. Это так называемый адрес

возврата

в про¬

грамму A . По окончании работы подпрограмма B извлекает из стека адрес возврата в программу A и возвращает управление по этому адресу. Таким образом, компьютер продолжает выполнение програм¬ мы A , начиная с инструкции, следующей за инструкцией вызова. В большинстве процессоров имеются специальные команды, поддержи¬ вающие вызов подпрограммы с предварительным помещением адреса возврата в стек и возврат из подпрограммы по адресу, извлекаемому из стека. Обычно команда вызова назывется call, команда возврата — return.

В стек помещаются также параметры подпрограммы или функ¬ ции перед ее вызовом. Порядок их помещения в стек зависит от соглашений, принятых в языках высокого уровня. Так, в языке Си

или

C + + на вершине стека лежит

первый аргумент функции, под

ним

второй и так далее. В Паскале

все наоборот, на вершине стека

лежит последний аргумент функции. (Поэтому, кстати, в Си возмож¬ ны функции с переменным числом аргументов, такие, как printf, а в Паскале нет.)

В Фортране-4, одном из самых старых и самых

удачных язы¬

ков программирования, аргументы передаются через

специальную

область памяти, которая может располагаться не в стеке, поскольку

до конца

70-х годов X X века еще существовали компьютеры вроде

I B M 360

или ЕС Э В М без

аппаратной реализации стека. Адреса

возврата

также сохранялись

не в стеке, а в фиксированных дл я каж¬

дой подпрограммы ячейках памяти. Программисты называют такую память статической в том смысле, что статические переменные за¬ нимают всегда одно и то ж е место в памяти в любой момент работы программы. При использовании только статической памяти рекур¬ сия невозможна, поскольку при новом вызове предыдущие значения локальных переменных разрушаются. В эталонном Фортране-4 ис¬ пользовались только статические переменные, а рекурсия была за¬ прещена. До сих пор язык Фортран широко используется в научных

4.4.2. Стек

259

// Прототипы функций, реализующих предписания стека:

void s t _ i n i t ( i n t maxSize); // Начать работу (вх: цел

//макс, размер стека)

void

st_terminate();

// Закончить

работу

void

st_push(double

x); // Добавить

эл-т (вх: вещ x)

double

st_pop();

// Взять элемент: вещ

double

st_top();

// Вершина стека: вещ

int

s t _ s i z e ( ) ;

// Текущий размер стека: цел

bool

st_empty();

// Стек пуст? : лог

int

st_maxSize();

// Макс, размер стека: цел

bool

st_freeSpace();

// Есть свободное место? : лог

void

s t _ c l e a r ( ) ;

// Удалить все элементы

double st_elementAt(int i ) ; // Элемент стека на

// глубине (вх: i ) : вещ

#endif

// Конец файла "streal.h"

Отметим, что директивы условной трансляции

#ifndef ST_REAL_H #define ST_REAL_H

#endif

используются дл я предотвращения повторного включения h-файла: при первом включении файла определяется переменная препроцессо¬ ра ST_REAL_H, а директива "#ifndef ST_REAL_H" подключает текст, только если эта переменная не определена. Такой трюк используется практически во всех h-файлах. Нужен он потому, что одни h-файлы могут подключать другие, и без этого механизма избежать повтор¬ ного включения одного и того ж е файла трудно.

Файл "streal.cpp" описывает общие статические переменные, над которыми работают функции, соответствующие предписаниям стека,

иреализует эти функции.

//Файл "streal.cpp"

//Стек вещественных чисел, реализация

260

4.4. Простейшие структуры данных. Стек. Очередь

//

<stdlib.h>

#include

#include <assert.h>

#include

"streal.h" // Подключить описания функций стека

//Общие переменные для функций, реализующих

//предписания стека:

static

double

*elements =0;

// Указатель на массив эл-тов

static

i n t max_size =0;

//

стека в дин. памяти

// Размер

массива

static

i n t sp

= (-1);

// Индекс

вершины стека

// Предписания

стека:

 

 

 

void s t _ i n i t ( i n t maxSize) {

//

Начать

работу (вх:

 

 

 

//

макс. размер стека)

assert(elements == 0); max_size = maxSize;

elements = (double *) malloc( max_size * sizeof(double)

);

sp = (-1);

}

 

 

 

 

 

void st_terminate()

{

//

Закончить

работу

i f (elements !=

0)

{

 

 

 

free(elements);

 

 

 

}

 

 

 

 

 

}

 

 

 

 

 

void st_push(double x) {

// Добавить эл-т (вх: вещ x)

assert(

0 &&

//

утв:

начал работу и

elements !=

//

стек

sp < max_size-1

//

есть

своб. место

);

 

 

 

 

 

++sp;

x;

 

 

 

 

elements[sp] =