Практика программирования (Бейсик, Си, Паскаль)

       

Параметры подпрограмм, локальные и глобальные данные


Для обеспечения максимальной независимости программных единиц во всех алгоритмических языках принято стандартное правило — данные, объявленные внутри программной единицы, являются собственностью этой программной единицы и образуют так называемый набор локальных данных. В QBasic и Си область действия локальных данных распространяется только на подпрограмму (функцию), в которой эти данные объявлены. Поэтому, несмотря на совпадение имен переменных, используемых в разных программных единицах, между такими переменными ничего общего нет. Это развязывает руки создателям программ, т. к. позволяет им не вникать в детали чужих программ, которые они собираются использовать. Наличие вложенных блоков в программах на Паскале немного модифицирует этот стандарт. Здесь действуют права наследования — любой внутренний блок имеет право использовать данные, объявленные во внешнем по отношению к нему блоке. Но если во внутреннем блоке объявляется переменная с таким же именем, как и во внешнем блоке, для нее выделяется новая ячейка оперативной памяти при одновременном сохранении значения переменной внешнего блока.

В связи с описанным разграничением данных возникает вопрос о механизмах взаимодействия программных единиц, одна из которых обращается к другой. Таких механизмов, в общем-то, два — передача данных через параметры и совместное использование общих (глобальных) переменных.

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

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




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

Как определить или задать тип параметра в подпрограммах и функциях на Бейсике? Обратите внимание на заголовок подпрограммы или функции. Имя каждого формального параметра либо сопровождается специальным знаком (% — короткий целый, & — длинный целый, ! — короткий вещественный, # -длинный вещественный, $ — строковый), либо за ним следует описатель типа вида AS type. В качестве описателя типа параметра может быть использовано одно из служебных слов INTEGER, LONG, SINGLE, DOUBLE или STRING.

Если параметром является массив, то, независимо от его размерности, вслед за именем располагаются пустые скобки. Для определения минимального и максимального значения индекса по каждому измерению фактического массива, который подпрограмма получит во время работы, следует использовать функции LBOUND и UBOUND. Первым аргументом таких функций является имя формального массива (а во время выполнения вместо него будет подставлено имя фактического массива), а вторым — порядковый номер индекса для многомерных массивов. Если формальный параметр А представлен одномерным массивом, то обращения LBOUND(A, 1) и LBOUND (А) эквивалентны.

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


В отличие от этого в системах компилирующего типа, к которым относятся и Turbo С, и Turbo Pascal, ситуация с формальными параметрами процедур и функций определена более жестко. В качестве любого конкретного пара-

метра можно передать либо адрес соответствующего фактического аргумента, либо его значение. Для параметров, принимающих только адреса, в Си используются указатели, а в Паскале — аргументы, снабженные описателем var. Все остальные параметры передаются только по значению. Чтобы предотвратить изменение фактического параметра, переданного по адресу, в списке аргументов перед описанием соответствующего формального параметра должно находиться служебное слово const.



Кроме аппарата параметров, программные единицы могут совместно использовать значения общих (глобальных переменных).

В QBasic объявление глобальных переменных, доступных в любой внешней подпрограмме или внешней функции, сопровождается добавкой SHARED (дословно - "совместно используемый"):

DIM SHARED A(20)
AS INTEGER, D
AS SINGLE
COMMON
SHARED A(20)
AS INTEGER, D
AS SINGLE

Последнее объявление означает, что общие переменные (массив А и одиночная переменная о) будут доступны не только всем подпрограммам и функциям данного программного файла, но и другим Бейсик-программам, загружаемым в память по оператору CHAIN.

В программных файлах Си переменные, объявленные за пределами всех функций, считаются глобальными по отношению к функциям этого файла и могут использоваться в теле любой функции без каких-либо дополнительных указаний. Но если в теле функции объявлена переменная с таким же именем, то зона ее влияния локализована в данной функции и глобальная переменная с аналогичным именем здесь уже не действует. Если же задача собирается из нескольких программных файлов, общие для всех функций переменные целесообразно выделить в отдельный программный файл. В функциях, где предполагается их использование, такие переменные должны объявляться с описателем extern (внешний):

extern int a[20]; extern float d;

В программе на Паскале все переменные головной программы являются глобальными. К таковым же относятся и все переменные, объявленные в модулях, подключаемых к программе с помощью директивы uses. А дальше действует описанный выше стандарт — переменные любого блока могут выступать в качестве глобальных по отношению ко всем вложенным блокам.



Содержание раздела