Дата публикации: Feb 04, 2013 5:48:53 PM
Концепция структурного программирования не рекомендует использования глобальных данных, то есть объектов данных, доступных непосредственно более, чем одной программной единице. Непосредственно - то есть не через аргументы процедуры и не по указателю. Фортран позволяет придерживаться этой концепции, поскольку процедуры могут быть функциями, причем функции могут возвращать как скаляры, в том числе производных типов, так и массивы и указатели. Однако списки возвращаться не могут (и вообще концепция списка отсутствует), поэтому непосредственно вернуть, например, массив, скаляр и еще один массив функция не может. Взамен Фортран позволяет возвращать значения в исходящих параметрах. Так, математическую функцию y=f(x) можно реализовать как функцию F(X) и вычислять значения выражением Y=F(X); либо как подпрограмму F(X,Y) с входящим X и исходящим Y. Атрибут PURE объявляет процедуру стерильной - она не может менять что-либо вне своего тела, что по существу отменяет для нее возможность изменять глобальные данные.
Однако во многих случаях глобальные данные удобны и эффективны. Большое число параметров процедур, работающих с одними и теми же данными, трудно сопровождать, а затраты на передачу параметров могут быть велики. Фортран открывает несколько возможностей для организации глобальных данных.
Первой возможностью является связь по вложенности: внутренние процедуры основной программы, отделенные от тела словом CONTAINS (но перед завершающим END) имеют доступ к объектам данных основной программы. В небольшой программе типа "скрипта" это может быть удобно: небольшие процедуры-утилиты, совершающие действия над глобальными объектами, реализованы как внутренние, а основной алгоритм вызывает их по мере необходимости.
Основной, рекомендованной возможностью организации глобальных данных являются модули. Объекты данных (а также типы и процедуры), объявленные в модуле с атрибутом PUBLIC (назначаемым по умолчанию) доступны в любой области видимости, в которой модуль импортирован оператором USE. При этом возможно переименование объектов, а ключевое слово ONLY позволяет частичный импорт. Эти возможности помогают защитить одноименные объекты в разных областях видимости. К примеру, переменная E может присутствовать в модуле, а также в процедуре и основной программе, и иметь разный смысл. Импортируя модуль в программу, можно переименовать переменную: USE MOD1, E => energy; а в процедуре, предположим, необходимости в переменной e из модуля нет, поэтому можно вообще не импортировать: USE MOD1, ONLY: A; разные программные единицы могут работать с данными модуля и вызывать модульные процедуры.
Устаревшее, классическое, до сих пор используемое средство - это общие блоки COMMON. Объявленный блок с данным именем (или безымянный - он один, если есть) определяет одну и ту же область памяти для всех программных единиц, в которых определен. Если перечисленные в нем объекты данных имеют тождественные объявления, то получаются глобальные объекты, доступные из всех областей видимости, в которых объявлен блок. Если объекты совпадают по типу и конфигурации, то получаются глобальные объекты "с переименованием": в одной единице объект объявлен под одним именем, в другой - под другим, но это один и тот же объект, потому что занимает одну и ту же область памяти. Возможны и различные объекты, что до некоторой степени эмулирует указатели. Например, возможен доступ к элементам массива, объявленного в основной программе, по имени, если соответствующий блок в процедуре содержит несколько скаляров. Возможен доступ с столбцам матрицы (но не строкам!). Можно даже объявлять данные разных типов и блоки разной длины для связи по памяти. Когда-то эти трюки могли быть полезны, однако ныне единственная полезная функция общих блоков - объявление глобальных данных, но и та вытесняется более мощным и удобным средством - модулями.
Модули удобнее уже потому, что данные в модуле объявляются единожды (а затем модуль импортируется), а общие блоки надо объявлять в каждой области видимости. Конечно, это решается включением другого файла в данный (INCLUDE), но тем не менее. Модуль может содержать процедуры для работы с данными, а также скрытые (PRIVATE) процедуры, вызываемые открытыми и невидимые вне модуля. Данные, которые используются только модульными процедурами, можно также скрыть. Еще есть защищенные (PROTECTED) данные, которые можно читать, но нельзя изменять вне модуля. Из могут менять только модульные процедуры. Это гарантирует, например, согласованность: изменение данных возможно только посредством вызова процедуры, а та способна менять и другие данные, обеспечивая их согласованность. Пример: температура в различных шкалах.
Для инициализации данных предусмотрен оператор DATA. Он почти полностью перекрывается возможностью инициализации данных при объявлении, однако иногда стилистически выгоднее определять данные отдельно, а кроме того, DATA дает возможность определять часть массива. Для объявления общих блоков и инициализации входящих в них объектов предусмотрена специальная программная единица - блок данных (DATA BLOCK). Однако модули удобнее, тем более что DATA допустим и в них.
К теме глобальных данных примыкает атрибут SAVE. Он назначается объектам в процедурах или других областях видимости, которые могут перестать существовать. При этом объект остается неизменным, как если бы он был глобальным. Однако доступ к нему имеет только объявившая его процедура. это удобно, когда объек данных должен хранить свое значение между вызовами процедуры, однако использует его только одна процедура, в связи с чем объявлять глобальный объект нерационально. Атрибут SAVE назначается по умолчанию, если объект данных инициализируется. Так, такая процедура
SUBROUTINE F
INTEGER:: I=0
I = I + 1
PRINT*, I
END
будет печатать номер своего вызова, тогда как такая:
SUBROUTINE F
INTEGER:: I
I = 0
I = I + 1
PRINT*, I
END
выводит всегда единицу.