Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
паскаль.doc
Скачиваний:
7
Добавлен:
25.09.2019
Размер:
222.21 Кб
Скачать

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}