CFA LogoCFA Logo Computer
Загрузка поиска
Новости Компьютеры Прайс-лист [Новое] Прайс-лист [Б/У] Для ноутбуков Конфигуратор ПК Заказ, Оплата, Доставка Сервис объявления Драйвера Статьи Как нас найти Контакты
Новости
RSS канал новостей
Специалисты компании iiyama предлагают своим клиентам новый крупноформатный монитор. Речь идет о ...
Производители материнских плат уже давно озаботились тем, чтобы предложить своим клиентам идеальную ...
Специалисты компании Epox предлагают своим клиентам новенькую материнскую плату компактных размеров ...
Итак, состоялся анонс восьмого поколения смартфонов Apple iPhone вместе с юбилейным iPhone X. И ...
Сколько, по вашему мнению, должно быть камер в смартфоне? Huawei, похоже, считает, что можно установить ...
Самое интересное
Программаторы 25 SPI FLASH Адаптеры Optibay HDD Caddy Драйвера nVidia GeForce Драйвера AMD Radeon HD Игры на DVD Сравнение видеокарт Сравнение процессоров

АРХИВ СТАТЕЙ ЖУРНАЛА «МОЙ КОМПЬЮТЕР» ЗА 2003 ГОД

Мысли о Паскале

Владислав ДЕМЬЯНИШИН nitromanit@mail.ru

(Продолжение, начало см. МК №46, 51—52, 4, 6—7, 10, 12—13, 16—18, 22, 24, 29, 34, 41, 46 (165, 170—171, 175, 177—178, 181, 183—184, 187—189, 193, 195, 200, 205, 212, 217)).
А теперь, продолжая рассмотрение процедур и функций, мы коснемся темы передачи параметров подпрограммам.

Механизм параметров подпрограмм

Как я уже говорил ранее, в заголовке процедуры и функции может быть задан список формальных параметров:

Вообще, следует быть особенно внимательным к эквивалентности типов (см. раздел «Преобразование типов. Совместимость типов», МК №6-7, 10 (177-178, 181)), т.е. типы параметров должны в обязательном порядке обозначаться идентификаторами. Например, следующий заголовок недопустим:

так как вызовет ошибку компиляции Error 12: Type identifier expected (не указан идентификатор типа).

В случае необходимости передачи в подпрограмму параметра с типом, определенным программистом, следует указать его идентификатор (имя), например:

Turbo Pascal предоставляет три способа задания параметров подпрограмм. Например, все параметры подпрограммы могут быть заданы одним из трех способов, или каждый параметр может быть задан любым из трех способов.

Параметры-значения

Параметр-значение (передача параметров по значению) — это самый простой способ передачи параметров. Он осуществляется следующим образом. Перед выполнением подпрограммы в стеке создается локальная переменная, которая инициализируется соответствующим значением фактического параметра, указанного в вызове подпрограммы. Т.е. если в вызове в качестве параметра подпрограммы указана константа или переменная, то ее значение заносится в данную локальную переменную (формальный параметр). Если же в вызове указано выражение, то предварительно производится вычисление результата данного выражения, и значение результата заносится в локальную переменную.

А теперь поясню для тех, кто не въехал :-):

Формальный параметр — это параметр, описанный в заголовке подпрограммы, т.е. который должна получить подпрограмма при вызове.

Фактический параметр — это параметр, который указан непосредственно в вызове подпрограммы, и значение которого передается. Это может быть непосредственное значение, константа, переменная, выражение, имеющее тип, совместимый по присвоению с соответствующим формальным параметром в заголовке подпрограммы.

А теперь вернемся к нашим баранам.

Таким образом, с локальной переменной можно делать все что угодно. «Она будет сопротивляться, кусаться, кричать: «Я буду жаловаться в райком!» Но это — красивый кавказский обычай». Тьфу, занесло :-).

Но главное в этом способе то, что какие бы действия не выполнялись над локальной переменной (формальным параметром), это никак не отразится на значении фактической переменной, указанной в вызове подпрограммы. Рассмотрим пример процедуры, использующей передачу параметров по значению:

Если вызвать эту процедуру, например, так:

то значения фактических параметров (переменных) X и Y копируются один раз в соответствующие формальные параметры (переменные) A и B. Внутри процедуры происходит приращение значения переменной A на величину значения переменной B, при этом значение внешней переменной X остается неизменным. Это своего рода изоляция внешних переменных от локальных преобразований.

Еще хочу привести пример с массивом. Пусть

тогда необходимо учитывать тот факт, что передача массива B происходит по значению, т.е. ,как я уже говорил выше, весь массив B загружается в локальную переменную-массив A. К чему я все это? Да к тому, что локальные переменные располагаются в стеке, а стек, как известно, не резиновый. А значит, в ходе выполнения программы с многочисленными вложенными вызовами процедуры MyProc очень скоро может наступить момент переполнения стека: Error 202: Stack overflow error. Я уже не говорю о том, что эта ошибка будет возникать всегда при установке, например, директивы {$M 10000,..,..}. Еще один момент, который нельзя сбрасывать со счетов — это быстродействие такого способа передачи параметра при работе с большим массивом. Ведь при загрузке значений массива B в массив A выполняется поэлементное копирование массива B, что при многократном вызове данной процедуры потребует дополнительных затрат времени.

Параметры-переменные

Параметр-переменная — это способ передачи фактического параметра с обратной связью, его еще называют передачей параметра по ссылке. Для задания такого способа необходимо указать служебное слово var перед именем (или списком имен через запятую) параметра в списке параметров подпрограммы. Такой способ позволяет манипулировать значениями внешних переменных, указанных в вызове подпрограммы, как с локальными, но все изменения значений этих якобы локальных переменных приведут к изменению значений соответствующих внешних. Таким образом, описывая процедуру, можно сделать так, чтобы она возвращала в точку вызова результат, а то и несколько результатов одновременно.

Поясню это на примере. Допустим, необходимо создать процедуру, которая определяла бы минимальное и максимальное значение элементов массива:

Нахождение минимума и максимума обеспечено. Однако результаты заносятся в локальные переменные Min и Max, которые известны только в пределах текущего блока. А ведь нам необходимо передать результаты поиска во внешний блок программы.

Тогда попробуем так:

и исправим основной блок программы

и снова потерпим неудачу, так как хоть переменные Min и Max являются параметрами, но параметрами-значениями, и значит, их значения не будут переданы во внешний блок программы. Т.е. в данном случае необходимо использовать передачу параметров по ссылке:

В данном случае формальные параметры Min и Max считаются синонимами соответствующих фактических параметров в пределах процедуры. Следует помнить, что фактические параметры должны быть переменными (и ни в коем случае не выражениями и нетипизированными константами) того же типа, что и формальные параметры. Теперь присваивания параметрам Min и Max внутри блока процедуры будут эквивалентны соответствующему присваиванию внешним переменным MinB и MaxB, которые были переданы процедуре как параметры-переменные. По завершении выполнения процедуры эти переменные из внешнего блока будут содержать соответствующие значения.

Совершенно очевидно, что в результате выполнения данного примера минимальным найденным значением будет 10 а максимальным — 245.

В соответствии с тем, что уже было сказано выше о загрузке массива в стек, гораздо эффективнее и безопаснее будет использовать следующий заголовок процедуры

Механизм передачи параметров при данном способе основан на том, что происходит передача не самого значения фактического параметра, а загрузка в стек адреса переменной, указанной в вызове подпрограммы. Поэтому все изменения значений отражаются не в области стека, а в памяти, где расположена переменная, переданная в качестве параметра.

Таким образом, применительно к нашему примеру, при передаче массива как параметра по ссылке происходит загрузка в стек не 10001 байта, а всего лишь четырех байт адреса этого массива, что выполняется гораздо быстрее и не требует большого размера стека.

Вот мы и проследили за эволюцией нашей процедуры MinMax.

Хочу заметить, что переменные файловых типов (см. главу «Файловые типы и ввод-вывод») могут передаваться в подпрограммы только как параметры-переменные.

Еще раз повторюсь. При данном способе передачи параметров подпрограмме в качестве фактического параметра-переменной нельзя передавать константы и выражения, но можно — типизированные константы и переменные. Иначе произойдет ошибка компиляции Error 20: Variable identifier expected (не указан идентификатор переменной).

Бестиповые параметры

Данный способ передачи параметров — это способ, при котором тип параметра не указывается, т.е. нет привязки к конкретному типу данных. В данном случае описание формального параметра в заголовке подпрограммы имеет следующий вид:

где Buf — имя формального параметра.

При вызове подпрограммы фактическим параметром может быть только переменная или типизированная константа любого типа, но ни в коем случае не выражение, так как при данном способе передача параметров происходит по ссылке.

Ввиду того, что тип формального параметра не указан, параметр несовместим ни с какой другой переменной, т.е. не может применяться ни в каких конструкциях. Чтобы иметь возможность работать с таким параметром, необходимо использовать либо операцию приведения типа (см. раздел «Преобразование типов. Совместимость типов», МК №6-7, 10 (177-178, 181)), либо описать локальную переменную конкретного типа с совмещением ее в памяти с нетипизированным параметром.

В качестве примера первого способа использования нетипизированных параметров напишем свой вариант стандартной процедуры Move:

Для демонстрации второго способа использования нетипизированных параметров напишем еще один вариант процедуры Move:

где локальная переменная ASrc размещается по адресу в начале области памяти параметра Src, а переменная ADest — соответственно, в области памяти параметра Dest.

По коду, полученному в результате компиляции, оба способа использования нетипизированных параметров абсолютно идентичны.

Данный способ передачи параметров дает программисту полную свободу действий над параметрами, но свободой тоже нужно уметь пользоваться правильно. Если, например, изменить описание строк так:

то в результате выполнения последней программы получим строку s1='Good day World!', где окончание строки будет размещаться в области переменной s2, тем самым строка s2 будет искажена, так как в примере не учитывается размер области назначения пересылки данных, т.е. не учитывается размер памяти, отведенный под переменную-получатель s1. Ведь под переменную s1 выделено 13 байт, а под s2 выделено 256 байт. А так как эти переменные в сегменте данных размещены по соседству, то происходит вот такая накладка. И дело вовсе не в нашей процедуре MyMove, а в аккуратности ее использования, так как аналогичный вызов стандартной процедуры Move даст тот же ошибочный результат.

Чтобы такой накладки не случалось, следует делать предварительную проверку перед применением процедуры Move или MyMove:

(Продолжение следует)

Рекомендуем ещё прочитать:






Данную страницу никто не комментировал. Вы можете стать первым.

Ваше имя:
Ваша почта:

RSS
Комментарий:
Введите символы: *
captcha
Обновить





Хостинг на серверах в Украине, США и Германии. © www.sector.biz.ua 2006-2015 design by Vadim Popov