- •Федеральное агентство по образованию
- •Структурированные типы данных. Классификация
- •Массивы Определение массива
- •Обращение к элементу массива. Хранение элементов массива. Доступ к элементам массива
- •Действия над массивами
- •Поиск элемента (одномерного ) массива Поиск среди неупорядоченных элементов массива
- •Поиск среди упорядоченных элементов массива
- •Сортировка элементов (одномерного) массива
- •Линейная сортировка (сортировка отбором)
- •Сортировка методом пузырька
- •5 4 3 1 2
- •5 4 3 2 1 Метод быстрой сортировки с разделением
- •Множества
- •Объявление множеств
- •Представление в памяти переменной типа множество
- •Операторы для работы с множествами Проверка принадлежности элемента множеству
- •Операции над множествами
- •Сравнение множеств
- •Применение множеств
- •Процедуры и структурное программирование
- •Преимущества структурного программирования
- •Планирование структурированной программы
- •Метод программирования сверху вниз
- •Определение процедуры
- •Передача управления при вызовах процедур и функций
- •Функции: подпрограммы,возвращающие единственный результат
- •Понятие блока
- •Область действия и время жизни переменных
- •Особенности локальных переменных
- •Особенности глобальных переменных
- •Особенности использования процедур и функций в турбо паскале
- •Опережающее определение процедур и функций
- •Рекурсия и итерация
- •Процедуры и функции как параметры
- •Директивы подпрограмм
- •Отладка и тестирование программ, содержащих подпрограммы
- •Нисходящее тестирование и подпрограммы-заглушки
- •Восходящее тестирование и программы-тестеры
- •Рекомендации по отладке программ, содержащих подпрограммы
- •Использование отладчикадля трассировки процедур
- •Запуск внешних программ
- •Стандартные модули
- •Модуль Crt
- •Модуль Graph
- •Функции
- •Текстовые файлы
- •Нетипизированные файлы
- •Типизированные файлы
- •Прямой доступ
- •Дополнительные функции работы с файлами
- •Обработка ошибок ввода-вывода
- •Указатели и динамические переменные Статические и динамические переменные
- •Адресация памяти в Турбо Паскале
- •Карта памяти Турбо Паскаля
- •Указатели
- •Операция для получения адреса
- •Функции для работы с адресами
- •Процедуры для работы с указателями
- •Присваивание значений указателям
- •Организация ссылок
- •Динамические структурированные переменные Динамические записи
- •Динамические массивы
- •Массивы размером более 64 кбайт
- •Строки с завершающим нулем (asciiz)
- •Процедуры и функции модуля strings
- •Указатели на процедуры и функции
- •Динамические структуры данных
- •Линейные списки
- •60 Лекции по курсу «Языки программирования» Часть II
Указатели на процедуры и функции
Указатель на подпрограмму определяется как переменная процедурного типа, например:
type
fun = function(x : real) : real: { процедурный тип }
var
pf : fun; { указатель на функции типа fun }
Указателю на подпрограмму можно присвоить NIL, значение другого указателя того же типа или имя конкретной подпрограммы. Присваивание имени выполняется без использования операции взятия адреса @, поскольку имя подпрограммы представляет собой адрес точки входа, то есть является константой процедурного (функционального) типа:
type
fun = function(x : real) : real: { функциональный тип }
var
pf : fun: { указатель на функции типа fun }
function f(x : real) : real; far: { конкретная функция }
begin
<тело функции>
end;
begin
…
pf:=f;
…
end.
После выполнения этого фрагмента программы в переменной pf будет храниться адрес точки входа в функцию f. Теперь функцию f можно вызвать через переменную pf обычным образом, например:
у := pf(x);
Обратите внимание, что функция, адрес которой присваивается переменной, должна компилироваться в режиме дальней адресации. Для этого в заголовке указывается директива far или задается ключ компиляции {$F+}.
Примечание. Это требование связано с тем, что переменная-указатель должна содержать полный адрес, состоящий из сегмента и смещения, а по умолчанию адрес подпрограммы содержит только смещение, потому что компилятор формирует всего один сегмент кода.
Пример. Шаблон программы, использующей массив указателей на функции. Такие массивы часто применяются при создании меню.
program mas_fun;
type
fun = function(x : real) : real;
function fl(x : real) : real; far;
begin
fl := sin(x);
end;
function f2(x : real) : real; far;
begin
f2 := cos(x);
end;
function f3(x : real) : real; far;
begin
f3 := arctan(x);
end;
const
pf : array[1..3] of fun = (fl, f2, f3);
var
y : real;
i : integer;
begin
for i := 1 to 3 do
writeln(' Результат функции ', i:2, ' : ', pf[i](PI));
end.
Динамические структуры данных
Если до начала работы с данными невозможно определить, сколько памяти потребуется для их хранения, память следует распределять во время выполнения программы по мере необходимости отдельными блоками. Блоки связываются друг с другом с помощью указателей. Такой способ организации данных называется динамической структурой данных, поскольку она размещается в динамической памяти и ее размер изменяется во время выполнения программы.
Из динамических структур в программах чаще всего используются линейные списки, стеки, очереди и бинарные деревья. Они различаются способами связи отдельных элементов и допустимыми операциями. Динамическая структура, в отличие от массива или записи, может занимать несмежные участки оперативной памяти.
Элемент любой динамической структуры состоит из двух полей: информационного поля и поля связи - указатели, обеспечивающие связь элементов друг с другом.
Элемент динамической структуры описывается в виде записи, например:
type
pnode = ^node;
node = record
d : word; { информационная }
s : char; { часть }
p : pnode; { указатель на следующий элемент }
end;
Примечание. Обратите внимание, что тип указателя pnode на запись node определен раньше, чем сама запись. Это не противоречит принципу «использование только после описания», поскольку для описания переменной типа pnode информации вполне достаточно.
Рассмотрим принципы работы с основными динамическими структурами.
Стеки
Стек является простейшей динамической структурой. Добавление элементов в стек и выборка из него выполняются из одного конца, называемого вершиной стека. Другие операции со стеком не определены. При выборке элемент исключается из стека.
Стек реализует принцип обслуживания LIFO (last in — first out, последним пришел — первым обслужен). Стеки широко применяются в системном программном обеспечении, компиляторах, в различных рекурсивных алгоритмах.
Для работы со стеком используются две статические переменные: указатель на вершину стека и вспомогательный указатель.
var
top,
p : pnode;
Тип указателей должен соответствовать типу элементов стека. Мы будем строить стек из элементов, тип которых описан в предыдущем разделе.
Занесение первого элемента в стек выполняется в два приема: сначала выделяется место в памяти и адрес его начала заносится в указатель на вершину стека (оператор 1), а затем заполняются все поля элемента стека (операторы 2):
n
ew(top);
{ 1 }
top^.d := 100; { 2 }
top^.s : = 'а';
top^.p := nil;
Значение nil в поле указателя на следующий элемент говорит о том, что этот элемент в стеке является последним (см. рис.).
При добавлении элемента в стек кроме создания элемента и заполнения его информационной части (операторы 1 и 2) требуется связать его с предыдущим элементом (оператор 3) и обновить указатель на вершину стека (оператор 4), потому что теперь в вершине стека находится новый элемент:
n
ew(p);
{ 1 }
p^.d := 10; p^.s := 'b'; { 2 }
р^.р := top; { 3 }
top := р; { 4 }
Стек, состоящий из трех элементов, изображен на следующем рисунке.
Выборка из стека состоит в получении информационной части элемента (оператор 1), переносе указателя на вершину стека на следующий элемент (оператор 2) и освобождении памяти из-под элемента (оператор 3):
with top^ do writeln (d. s); { 1 }
p := top; top := top^.p: { 2 }
dispose(p); { 3 }
Рассмотренные операции удобно оформить в виде отдельных функций. Ниже приведена программа, которая формирует стек из пяти целых чисел и их текстового представления и выводит его на экран. Функция занесения в стек по традиции называется push, а функция выборки — pop.
program stack;
const
n = 5;
type
pnode = ^node;
node = record { элемент стека }
d : word;
s : string;
p : pnode;
end:
var
top : pnode; { указатель на вершину стека }
i : word;
s : string;
const text : array [1.. n] of string =
('one', 'two', ' three', 'four', 'five');
{---------------------------------- занесение в стек ------------------------------------}
function push(top var p : pnode; const s : string): pnode;
var p : pnode;
begin
new(p);
p^.d := d; p^.s :=s; p^.p := top;
push := p;
end;
{---------------------------------- выборка из стека ------------------------------------}
function pop(top : pnode; var d : word; var s : string): pnode;
var p : pnode;
begin
d := top^.d; s := top^.s;
pop := top^.p;
dispose(top);
end;
{---------------------------------- главная программа ------------------------------------}
begin
top := nil;
{ занесение в стек }
for i := 1 to n do top := push(top, i, text[i]);
{ выборка из стека }
while top <> nil do
begin
top := pop(top, i, s);
writeln(i:2, s);
end;
end.
Обратите внимание на то, что вспомогательный указатель объявлен внутри функций, а их интерфейс содержит все необходимые сведения: с каким стеком идет работа и что в него заносится или выбирается. Функции возвращают указатель на вершину стека.
