- •1. Общая структура программ в тп 7.0
- •2. Раздел объявлений и соглашений
- •3. Раздел текстов процедур и функций
- •4. Заголовок программы
- •Операторы языка Pascal
- •1. Составной и пустой операторы
- •2. Операторы ветвлений
- •3. Операторы повторений
- •Простые и структурные типы данных
- •1. Перечисляемый и ограниченный типы
- •2. Символьные строки
- •3. Множества
- •4. Записи
- •5. Запись с вариантами
- •6 Совместимость и преобразования типов
- •7 Типизированные константы
- •Процедуры и функции
- •1. Блочная структура программ
- •2. Общая структура подпрограммы
- •3. Области видимости объектов
- •4. Механизм передачи параметров
- •5. Предварительные и внешние описания подпрограмм
- •6. Специальные случаи
- •7. Рекурсия и побочный эффект
- •8. Распределение памяти под данные
- •9. Процедурные типы
8. Распределение памяти под данные
Память под глобальные и типизированные константы выделяется при запуске программы в одном сегменте (65520 байт - сегмент данных программы). Локальные переменные динамически размещаются в том же сегменте при активизации подпрограммы. При рекурсии или просто вызове подпрограммы используется стек. Через стек передаются параметры подпрограмм, в стеке сохраняются переменные, вышедшие за область видимости при передаче управления в подпрограмму. Причем дописывание информации в стек происходит при каждом рекуррентном вызове , поэтому нужно следить за глубиной рекурсии, иначе слишком длинная, или бесконечная рекурсия забьет все стековое пространство и программа не сможет выполняться дальше, произойдет ошибка времени исполнения. Максимальный объём стековой памяти в Турбо-Паскале - 1 сегмент (65520 байт). Можно устанавливать размер стека и контроль за переполнением стека - $M и $S (тоесть, компилятор может включить в программу код, следящий за переполнением стека и выдающий вовремя сообщение о переполнении стека, вместо зависания программы, обычно этот ключ установлен по умолчанию). Но не следует всегда выставлять непременно максимальный обьем стека, так как ето приводит к увеличению размера памяти, занимаемой программой при исполнении.
9. Процедурные типы
Турбо-Паскаль позволяет вводить переменные специального типа, значениями которых могут служить подпрограммы. Можно интерпретировать подпрограмму как значения, которые можно присваивать переменным и передавать их в качестве параметров (речь идёт о подпрограммах как о целостных объектах, а не о значениях, возникающих в процессе их выполнения).
{$F+} Var P: Procedure;
Значениями Р могут быть любые процедуры без параметров. В более общем случае: Type Func = Function(X,Y: Integer): Integer; Var F1,F2: Func;
Например, если есть функция: Function Add(A,B: Integer): Integer; Begin Add:= A + B; End;
то допустимо F1:= Add, в этом случае переменной F1 присваивается функция Add как таковая, но её исполнения не происходит.Теперь можно: Write (Add (1,2)) или Write (F1 (1,2));.
Следует обратить внимание на строку {$F+} – она существенна, это ключ компилятора, и если он выключен, при присвоении переменным процедурного типа значений конкретных подпрограмм, возникнет ошибка присвоения типа.
Имена формальных параметров, указываемые в процедурных типах, играют часто иллюстративную роль и на смысл определений никакого влияния не оказывают. Необходимыми являются только идентификаторы типов параметров и результатов (для функций).
Процедурные типы допускают также присвоение вида F1:= F2; Такие переменные можно использовать для вызова подпрограмм, которые присвоены этим переменным.
Пример: Var Oper: Function(X, Y: Real): Real; Function Add(A,B: Real): Real; Begin Add:= A + B; End; Function Sub (A,B: Real): Real; Begin Sub:= A - B; End; тогда в основной программе можно использовать: If <условие> then Oper:= Add else Oper:= Sub; Write (Oper(2.5, X+Y));
Процедурные типы, чтоб они были совместимыми по присваиванию, должны иметь одинаковое количество формальных параметров, а параметры на соответствующих позициях должны быть одного типа. Также, должны совпадать типы возвращаемых значений у функций.
Использование процедурных типов не ограничиваются простыми процедурными переменными. Как и любой другой тип, процедурные типы могут участвовать в построении структурированных типов:
Type Proc = Procedure(T: Real); Notice = Record Next: Integer; Time: Real; Action: Proc; End; Var New_Notices: Array[1..10] of Proc; Notices: Notice;
Правила корректной работы с процедурными типами.
Подпрограмма, присваиваемая процедурной переменной должна быть странслирована в режиме “дальнего вызова”.
Подпрограмма, присваиваемая процедурной переменной, не должна быть стандартной процедурой или функцией. Это ограничение легко обойти: Var Func: Function(R: Real): Real; . . . . . . . . . . . . . . . . Function MyExp(R: Real): Real Begin MyExp:= Exp (R); End; . . . . . . . . . . . . . . . . Begin Func:= MyExp;
Подпрограмма, присваиваемая процедурной переменной, не может быть вложенной в другие подпрограммы.
Подпрограмма, присваиваемая процедурной переменной, не может быть подпрограммой специального вида (interrupt или inline).
Процедурная переменная занимает в памяти 4 байта (2 слова). В первом хранится смещение, во втором - сегмент (т.е. указатель на код подпрограммы).
Можно описывать процедуры и функции, параметрами которых являются процедурные переменные. Таким образом, можно организовать подпрограмму, которая будет выполнять некоторое общее действие для различных подпрограмм - параметров. В общем случае использование процедурных переменных в операторе или выражении означает вызов присвоенной данной переменной процедуры или функции.
Необходимо помнить, что если в левой части оператора стоит процедурная переменная, правая часть его должна представлять идентификатор другой процедурной переменной или идентификатор подпрограммы.
К сожалению есть неопределённость для компилятора, например в таком случае: Type Func = Function: Real; Var F: Func; Function FFF: Real; Begin FFF:= 1.25; End; Function FF: Real; Begin FF:= 2.10; End; . . . . . . . . . . F:= FF; If F = FFF then . . . .
В подобных случаях неочевидно, должен ли компилятор сравнивать значение процедуры F с FFF или нужно вызвать процедуру F и FFF и сравнить их значения. Принято, что такое вхождение идентификатора подпрограммы означает вызов функции.
Чтобы сравнить значение переменной F со значением (адресом) подпрограммы FFF нужно использовать следующую конструкцию: If @F = @FFF then . . . .
Чтобы получить адрес самой процедурной переменной нужно написать: @@F
Приведение типов переменных для процедурных типов. Определены следующие типы и переменные: Type Func:= Function(X: Integer): Integer; Function MyFunc(X: Integer): Integer; Begin MyFunc:= X; End; Var F: Func; P: Pointer; N: Integer;
Можно построить следующие присваивания:
F:= MyFunc |
{переменной F присваивается функция MyFunc} |
N:= F(N) |
{функция MyFunc вызывается через переменную F} |
P:= @F |
{P получает указатель на функцию MyFunc} |
N:= Func (P)(N) |
{функция MyFunc вызывается через указатель P} |
F:= Func (P) |
{присвоить значение подпрограммы в P переменной F} |
Func(P):= F |
{присвоить значение подпрограммы в F указателю P} |
@F:= P |
{присвоить значение указателя в P переменной F} |