Обратите внимание на двоякое предназначение ключевого слова VAR. Во-первых, оно открывает секцию объявления переменных, а во-вторых, служит для указания ссылки на переменные в параметрах процедур.
Вернемся к программе. Если она остановилась в пошаговом режиме, прервите её комбинацией Ctrl+F2. Затем исправьте заголовок процедуры указанным выше манером, откомпилируйте программу и вновь пройдите по шагам. Находясь внутри процедуры Scan, вы заметите, что переменные arg и S теперь изменяются синхронно (рис. 53). Это то, что нам нужно, стало быть, проблема решена!
Рис. 53 – Синхронное изменение формального и фактического параметров
Далее можете «толкнуть» программу в непрерывном режиме, нажав комбинацию Ctrl+F9.
Итоги
• Для поиска ошибок применяют встроенный отладчик, который позволяет выполнять программу по шагам, а также просматривать переменные и менять их значения.
• При объявлении параметра без ключевого слова VAR, данные передаются только внутрь процедуры (по значению). Такой параметр используют как локальную переменную.
• Для передачи данных как внутрь процедуры, так и обратно, параметр объявляют с ключевым словом VAR. Тогда он служит ссылкой на другую переменную и меняется синхронно с нею.
А слабо?
А) Комбинация клавиш Ctrl+F8 устанавливает так называемые точки останова на исполняемых операторах. Эта же комбинацией отменяет их. Точка останова – это строка, на которой отладчик задерживает выполнение программы и ждет команды на её продолжение в непрерывном или пошаговом режиме..
Установите точку останова на выходе из процедуры Scan (на строке END) и запустите программу в непрерывном режиме (Ctrl+F9). Что произойдет? Чем, по-вашему, удобны точки останова?
Б) Перед запуском программы установите курсор внутри процедуры Scan и испытайте действие команды Run –> Go to cursor (клавиша F4).
Глава 22
О передаче параметров
Современные программы — даже не самые сложные — насчитывают тысячи строк. Как же распределена эта сложность? Почти вся она «размазана» по процедурам и функциям, а главную программу составляют обычно несколько строчек. Процедуры и функции, вызывая друг друга, передают данные словно эстафету по цепочке. Будущий профессионал должен овладеть тонкостями этого механизма.
Процедура обмена
Рассмотрим процедуру с несколькими параметрами. Пусть надо обменять значения в переменных A и B, это можно сделать так:
T:= A; { временно запомнить A }
A:= B;
B:= T; { поместить в B то, что раньше было в A }
Здесь T – переменная для временного хранения данных. Поручим эту простенькую работу процедуре, которую назовем Swap (обмен). Создавать процедуру начнем, как водится, с заголовка. Поскольку в обмене участвуют два числа, оба их надо передать через параметры. Для разделения формальных параметров используют точку с запятой. Если заголовок процедуры будет таким:
procedure Swap (x: integer; y: integer);
мы не добьемся своего, поскольку при передаче по значению результаты не вернутся в вызывающую программу. Правильным будет заголовок с двумя ссылками на переменные.
procedure Swap (var x: integer; var y: integer);
Если формальные параметры имеют одинаковый тип и способ передачи, то заголовок можно сократить так:
procedure Swap (var x, y: integer);
Принцип объединения в заголовке тот же, что при объявлении однотипных переменных в секции VAR.
Теперь напишем процедуру Swap и программу «P_22_1» для её проверки.
{ P_22_1 – процедура обмена и программа её проверки }
{ процедура обмена }
procedure SWAP(var x,y : integer);
var t: integer;
begin
t:= x; x:= y; y:= t;
end;
var A, B : integer;
begin {--- главная программа ---}
A:= 10; B:= 20;
Writeln(’A= ’, A, ’ B= ’, B);
SWAP(A, B);
Writeln(’A= ’, A, ’ B= ’, B);
Readln;
end.
Работает ли эта программа? Обязательно проверьте!
Замена символов в строке
Вернемся к программе P_20_1, где возможности процедуры Scan небогаты: допускается менять только символы «A» на символы «B». А если надо менять символы по своему усмотрению? Пожалуйста! Добавим в заголовок процедуры пару формальных параметров, например, так:
procedure Scan(var arg: string; Ch1, Ch2: char);
var k: integer;
begin
for k:=1 to Length(arg) do
if arg[k]= Ch1 then arg[k]:= Ch2;
end;
Здесь параметры Ch1 и Ch2 указывают, что и на что надо поменять. Поскольку параметры однотипны, они разделяются запятой. Порядок объявления формальных параметров в заголовке не важен. Но важно, чтобы при вызове процедуры порядок фактических параметров был таким же. Вот пример правильного вызова (символ «1» меняется на символ «2»).
Scan(S, ’1’, ’2’);
А вот ошибочные:
Scan(S, ’1’); { указаны не все параметры }
Scan(’1’, S, ’2’); { нарушен порядок следования параметров }
Scan(S, ’1’, ’2’, ’3’); { указан лишний параметр }
Scan(S, 1, 2); { неверный тип параметров }
За соответствием фактических параметров формальным жестко следит компилятор. Исключение составляют встроенные в язык процедуры ввода-вывода, такие как Readln и Writeln, где допускается гибкая передача параметров разных типов.
Переработайте программу «P_20_1» с тем, чтобы испытать новую версию процедуры замены символов, а затем исследуйте её в пошаговом режиме.
О передаче строк
Передача строковых данных таит свои тонкости. Рассмотрим процедуру Calc для подсчета заданного символа в некоторой строке.
procedure Calc(arg: string; Ch: char; var Res: integer);
var k: integer;
begin
Res:=0;
for k:=1 to Length(arg) do
if arg[k]= Ch then Res:= Res+1;
end;
Процедура принимает три разнотипных параметра: строку arg, символ Ch и ссылку на переменную Res – в ней возвращается результат. Здесь все правильно. Но недаром говорят: «меньше знаешь, – крепче спишь», – мой сон тревожит параметр arg строкового типа.
Поскольку строка может содержать до 255 символов, параметру arg отводится немалая память – 256 байтов! При передаче по значению все эти байты копируются в параметр arg, и на это тратится время. Если же параметр arg будет ссылкой на строку, то копирования не потребуется, и программа заработает быстрее. Вдобавок мы и память сэкономим, ведь ссылка на строку занимает в памяти всего 4 байта! Раз так, объявим процедуру иначе.