ПАСКАЛЬ: точка повернення, підстановка аргументів, зберігання змінних.
1. Точка повернення
Місце в програмі, що описує дію, якою вона продовжується по закінченні виконання виклику підпрограми, називається точкою повернення з підпрограми.
Отже, коли виконання програми доходить до виклику підпрограми, запам'ятовується точка повернення з неї, та вказівка на цю точку зберігається до закінчення виконання виклику. Потім за цією вказівкою продовжується виконання програми саме з точки повернення. Вказівка точки повернення займає частину локальної пам'яті процесу виконання виклику підпрограми. Що ще знаходиться в цій пам'яті, ми скажемо трохи нижче.
Точка повернення з процедури – це позначення дій, виконуваних після її виклику. Це може бути наступний за її викликом оператор, як у програмі sort31. Або умова завершення чи продовження циклу, якщо виклик записаний останнім у тілі циклу.
Після виклику функції може застосовуватися операція до значення, що повертається з виклику, або обчислюватися наступний операнд у виразі, або обчислене значення може використовуватися в операторі. Наприклад, у програмі minimdis із підрозділу 3.3 результат виклику функції присвоюється змінної dd. У програмі simpi із прикладу 4.7 виклик функції issimple записана як умова в if-операторі, і після виклику визначається, яка з гілок повинна бути виконана. При обчисленні виразу sqr(x)+sqr(y) спочатку один за одним виконуються виклики функції sqr, потім повернуті з них значення додаються.
Конкретний вигляд точки повернення в машинній програмі ми не уточнюємо.
2. Локальна пам'ять процесу виконання виклику підпрограми
У підрозділі 3.4 ми вже говорили про те, що параметрам-значенням підпрограми при виконанні її виклику зіставляються власні ділянки пам'яті, тобто змінні. У блоці підпрограми можуть бути означені власні імена змінних – ділянки пам'яті зіставляються їм так само. Крім того, є ділянка пам'яті з точкою повернення. Сукупність усіх цих ділянок пам'яті називається локальною пам'яттю процесу виконання виклику підпрограми. Її часто неточно називають локальною пам'яттю підпрограми. Будемо грішити цим і ми. Змінні в ній також називаються локальними.
Якщо підпрограма є функцією, то до її локальної пам'яті додається змінна для збереження значення, що повертається з її виклику. Можна сказати, що ця змінна ставиться у відповідність імені функції.
Таким чином, наприклад, локальна пам'ять процедури swap із підрозділу 3.4 (у її остаточному вигляді) складається з єдиної змінної, зіставленої імені t, а пам'ять функції dd із програми minimdis (підрозділ 3.3) – із змінних, зіставлених іменам xx1, yy1, xx2, yy2 і dd. А також указівки на точку повернення.
3. Підстановка аргументів на місце параметрів
Перед тим, як починається виконання операторів тіла підпрограми, для неї виділяється локальна пам'ять і в ній запам'ятовується точка повернення. Потім обчислюються значення тих аргументів у виклику, які відповідають параметрам-значенням. Ці значення присвоюються локальним змінним, які поставлено у відповідність параметрам-значенням.
Присвоювання значень аргументів, тобто копіювання їх у локальну пам'ять, називається підстановкою аргументів на місце параметрів за значенням.
Підстановка аргументів на місце параметрів-змінних відбувається зовсім іншим шляхом. Нагадаємо, що таким аргументом може бути тільки ім'я змінної (або інше її позначення, наприклад, у вигляді поля структури). Ніякого обчислення її значення, тобто розіменування аргументу, не відбувається.
Ця змінна, тобто ділянка пам'яті, ставиться у відповідність параметрові підпрограми. Це називається підстановкою аргументу на місце параметра за посиланням, або за адресою. У результаті такої підстановки
ім'я параметра-змінної перетворюється на вказівку на ділянку пам'яті, яка поставлена у відповідність аргументу.
Вказівка на ділянку пам'яті називається посиланням на неї, або її адресою. Як саме вказівка на аргумент стає відомою у процесі виконання підпрограми, ми не уточнюємо. Для цього нам довелося б занадто глибоко забратися в устрій комп'ютера і машинної мови.
4. Процес виконання виклику підпрограми
Опишемо процес виконання виклику підпрограми (вона називається такою, що викликається, а програма або підпрограма, що містить її виклик – такою, що викликає).
1. Виділяється локальна пам'ять підпрограми, що викликається, а точніше, пам'ять процесу виконання виклику.
2. Обчислюється й запам'ятовується в локальній пам'яті підпрограми, що викликається, точка повернення в ту, що викликає.
3. Обчислюються значення аргументів, відповідних параметрам-значенням, і посилання на пам'ять аргументів, відповідних параметрам-змінним. Виконується підстановка аргументів.
4. Лише після цього виконуються оператори тіла підпрограми. Зокрема, у локальній пам'яті запам'ятовується значення, що повертається з виклику функції.
5. Значення змінної з ім'ям функції копіюється з локальної в пам'ять програми, що викликає, або підпрограми, відведену під значення, що повертається. Процес виконання виклику підпрограми закінчується і продовжується виконання програми чи підпрограми, що викликає, із точки повернення.
Як уже говорилося в підрозділі 3.4, усяка зміна значення параметра-змінної є насправді зміною відповідного аргументу. Після закінчення виклику результати обробки параметрів-змінних залишаються в аргументах, чого не можна сказати про параметри-значення.
Змінні локальної пам'яті підпрограми не зіставлені ніяким іменам підпрограми, у якій записано виклик. А це значить, що після закінчення виконання виклику локальна пам'ять стає недоступною в підпрограмі, що викликає, і можна вважати, що вона звільняється. А раз так, то її можна наново зайняти під локальні змінні при виконанні наступного виклику якої-небудь підпрограми.
І останнє зауваження про те, як при написанні підпрограми вибирати вид параметра – параметр-значення чи параметр-змінна.
Якщо після виклику підпрограми повинно використовуватися нове значення аргументу, одержане при виконанні виклику, то відповідний параметр слід означити як параметр-змінну. Наприклад, як параметри в остаточному варіанті процедури swap із підрозділу 3.4.
Якщо після виклику підпрограми використовується старе значення аргументу або аргументом може бути довільний вираз, то йому має відповідати параметр-значення. Як параметр функції issimple з прикладу 4.7.
З останнього правила, утім, є винятки, пов'язані з параметрами та аргументами, що є масивами. Про це йтиметься в підрозділі 12.1.
Приклад 1. Розглянемо імітацію виконання програми з викликами підпрограм. Як і в підрозділі 7.1, будемо позначати локальну змінну, наприклад, L підпрограми F, як F.L, відрізняючи її від однойменної змінної програми. Вказівку точки повернення позначимо буквами ТП. Процес виконання програми
program nested ( input, output );
var a, b : integer;
function f ( x : integer ) : integer;
begin
x := x + 1; f := x
end;
function g (var x : integer ) : integer;
begin x := x div 2; g := x end;
begin a := 12; b := f ( g ( a ) ) end.
відобразимо такою таблицею:
Що виконується | Стан пам'яті | ||
a b | |||
a := 12 | 12 ? | ||
починається b := f(g(12)) | 12 ? | ||
виклик f(g(12)) | 12 ? | f. x f. f ТП | |
12 ? | ? ? b:= | ||
починається f. x := g(12) | 12 ? | ? ? b:= | |
виклик g(12) | g. x ? | ? ? b:= | g. g ТП |
g. x := 12 | 12 ? | ? ? b:= | ? f. x:= |
g. x := g. X div 2 | 6 ? | ? ? b:= | ? f. x:= |
g. g := g. X | 6 ? | ? ? b:= | 6 f. x:= |
повернення з g | 6 ? | ? ? b:= | 6 f. x:= |
закінчення f. x := g(12) | 6 ? | 6 ? b:= | |
f. x := f. x + 1 | 6 ? | 7 ? b:= | |
f. f := f. X | 6 ? | 7 7 b:= | |
повернення з f | 6 ? | 7 7 b:= | |
закінчення b := f(g(12)) | 6 7 |
Як видно з таблиці, перед виконанням виклику f(g(a)) у локальній пам'яті функції f запам'ятовується точка повернення – присвоювання b:=f(g(a)). Виконання виклику f(g(a)) починається обчисленням значення аргументу, відповідного її параметру x – виразу g(a). При цьому запам'ятовується точка повернення – присвоювання f.x:=g(a) – і починається виконання виклику функції g з аргументом a. Цей аргумент підставляється за посиланням, і зміна g.x є зміною a. З виклику функції g повертається значення 6 і присвоюється параметру f.x. Пам'ять функції g звільняється. Тільки тепер починається виконання операторів функції f. У результаті в програму повертається значення 7 і присвоюється змінній b. Пам'ять функції f звільняється.
Задачі
1.* Чи є правильною програма з прикладу 8.1, якщо замість оператора b:=f(g(a)) у її тілі записано
а) b:=g(f(a)); б) b:=f(a+33); a:=g(b); в) b:=f(g(a)+1); b:=f(f(a)+1).
Імітувати виконання нової програми, якщо вона правильна.
5. Автоматична пам'ять та програмний стек
Нагадаємо, що виконання програми починається після її завантаження в пам'ять комп'ютера. При цьому відбувається виділення пам'яті під її змінні. Ці змінні доступні з програми протягом усього часу її виконання, і тому називаються статичними. Область пам'яті, що виділяється під програму та її змінні, також називається статичною. Локальним іменам і параметрам-значенням підпрограм, як правило, зіставляють змінні з іншої області пам'яті – автоматичної. Ця назва пояснюється тим, що при виконанні викликів підпрограм ця пам'ять виділяється та звільняється "автоматично", без явних вказівок у програмі.