Теория
Сегодня перед вами откроется страшная тайна.... всё это время мы вас беспощадно обманывали !!!!!!!!!
:)
На самом деле всё не так плохо, просто решили, что не стоит сначала загружать народ не понятными терминами. Сегодня всё встанет на свои законные места.
Помните функцию ClrScr - так вот тайна её рождения состоит в том, что это ПРОЦЕДУРА.
Процедуры и функции очень похожи друг на друга. Основное отличие состоит в том, что функция может нам что-то возвратить, а процедура нет. ClrScr - ничего нам не возвращает => процедура, Round - возвращает => функция.
В некоторых языках (самый яркий пример - С/С++) вообще нет понятия процедуры - там только функции. При этом в качестве процедур используются функции которые ничего не возвращают. В Паскале же процедуры и функции разделили между собой.
Итак давайте научимся писать свои собственные процедуры и функции. Описание процедуры начинается со следующего заголовка:
procedure ИМЯ ( СПИСОК_ПАРАМЕТРОВ );
Список параметров может отсутствовать (например всё та же ClrScr - нет параметров). В остальном процедура очень похожа на программу. Кстате процедуры и функции вместе называют - под-программы!
Сегодня мы поговорим о процедурах. Плюсы в использовании процедур очевидны - не надо много раз повторять один и тот же код - достаточно просто указать имя процедуры. Давайте напишем такой пример:
procedure DisplayString;
var
i : integer;
begin
ClrScr;
for i := 0 to 10 do
writeLn ('Write something ....' )
end;
Как видите у процедуры есть своя секция переменных, секция меток, констант и секция выполняемых инструкций (секция кода). Код процедуры начинается соответственно со слова begin и кончается словом end. Обратите внимание, что после end мы поставили точку с запятой! Как вы помните точка - это признак конца программы, а программа не заканчивается процедурой!
Можно сказать, что до этого наша программа и была одной процедурой. Только без имени. Например в том же С - у этой фунции есть имя main.
Процедуры являются под-программами, поэтому они могут в свою очередь содержать под-под-программы, которые тоже могут содержать под-под-под-программы :) и так далее. Например структура нашей программы выглядела так (слева для наглядности нарисована схема, а справа написан текст программы):
|
Program ...;
begin
........
end. |
Если мы добавим две под программы, то она изменится в такую:
|
|
Program ...;
procedure A;
begin
.......
end;
procedure B;
begin
........
end;
begin
........
end. |
Если мы к под-программе А добавим две под-программы, то структура изменится в следующую строну:
|
|
Program ...;
procedure A;
procedure A1;
begin
........
end;
procedure A2;
begin
..........
end;
begin
.......
end;
procedure B;
begin
........
end;
begin
........
end. |
А если мы теперь к под-программе А2 добавим ещё пару под-программ, то структура превратится в следующее:
|
|
Program ...;
procedure A;
procedure A1;
begin
........
end;
procedure A2;
procedure A21;
begin
.......
end;
procedure A22;
begin
...........
end;
begin
..........
end;
begin
.......
end;
procedure B;
begin
........
end;
begin
........
end. |
А если мы ещё к под-программе А21 добавим три под-программы, то ....... впрочем хватит. Нужно знать меру :)
Существует так называемая видимость под-программ. Мы назваем под-программу видимой если можем её вызвать в противном случае она для нас невидима.
Например для основной программы видимы только 2 под-программы А и В. То что находится внутри их является "чёрным ящиком". Мы не знаем что там внутри А. Однако для под-программ А1 и А2 под-программа А является как бы родительской поэтому мы можем вызывать А1 и А2. Точно так же мы можем вызывать А21 и А22 только из А2.
При компиляции программы её текст просматривается компмлятором только один раз. Поэтому что бы вызвать процедру мы должны сначала её описать. Так в нашем примере из А нельзя вызвать В, т.к. читая сверху вниз мы не знаем что это такое. Но из В можно вызвать А.
Давайте теперь рассмотрим процедуру с параметрами : для объявления параметра мы должны указать его имя и тип. Параметры разных типов отделяются друг от друга точкой с запятой. Например:
procecure PrintString (x, y : integer; st : String);
begin
gotoxy (x, y);
write (st)
end;
...........
XS := 10;
YS := 10;
PrintfString (XS, YS, 'Hello world!');
Здесь в процедуру передаётся 3 параметра - X, Y, ST. Первые 2 типа integer и 3-ий типа string.
Однако нам может понадобится изменять параметры. При способе описанном выше переменные XS и YS не изменятся при выходе из процедуры, даже если добавим внутрь её что-то типа этого XS := 3; Этот метод передачи парамеров называется передача значения. В процедуру передаётся только значение XS и YS.
Существует и другой способ передачи параметров - адрессный. Тогда передаётся адресс переменной! Адресс - это точное указание места переменной в памяти (как например ваш почтовый адресс точно указывает где вы живёте). Так вот мы передаём этот самый адресс. И процедура может менять значение по этому адрессу. Т.е. мы поменяем значение переменой! Для этой цели нужно указать перед переменной слово var. Например:
procedure SQUARE (var x : integer);
begin
x := x * x
end;
var
i : integer;
begin
i := 2;
SQUARE (i);
writeLn (i)
end.
Выведет на экран 4. Т.к. мы меняем значение i (а вернее значение параметра) внутри процедуры. Если бы мы объявили процедуру так:
procedure SQUARE ( x : integer);
то тогда на экране появилась бы 2, т.к. мы не меняем значение параметра. Давайте мы рассмотрим ещё один пример: в позапрошлом выпуске мы говорили о том как поменять значения двух переменных (помните A = 5, B = 6 а надо A = 6, B = 5). Напишем такую проостую процедуру:
procedure swap (var a, b : integer);
var
c : integer;
begin
c := a;
a := b;
b := c
end;
Теперь её можно вызывать например так:
X := 3;
Y := 5;
swap (X, Y);
после этого X=5, а Y=3.
Вернёмся к области видимости. Теперь поговорим об области видимости переменных. Например что выведет такая программа:
var
i : integer;
procedure P;
var
i : integer;
begin
writeLn (i)
end;
begin
i := 2;
P
end.
Дествительно затруднительная ситуация. У нас есть 2 переменные с одним и тем же именем. Давайте условно назовём первую i (первую сверху) - i1, а вторую i2. Так вот i1 - является глобальной переменной по отношению к под-программе Р. Поэтому если представить, что мы не объявили i2, то на экран выведется 2. Переменная i2 называется локальной в под-программе Р. Считается, что локальные переменные "забивают" глобальные. Поэтому на экран выведется значение i2, а так как она не инициализированна, то это может быть что угодно (у меня 3631).
Поэтому нужно быть аккуратнее с именами. Иначе может случится казус :) при этом вы не всегда сможете быстро сообразить почему программа работает не правильно. Предпочтительнее давать глобальным и локальным переменным разные имена. Кстати всё выше сказанное относится и к константам и к меткам.
В Турбо Паскале допускается произвольная последовательность описания переменных, констант, меток, под-программ. Например мы можем несколько раз объявитеть секцию var (const, label) внутри под-программы. Например:
var
V1 : integer;
procedure S;
var
V2 : integer;
begin
.......
end;
var
V3 : integer;
..............
Здесь из под-программы S доступны переменные V1 и V2, а V3 нет (т.к. описание V3 следует после процедуры S).
|