О.
В.
Бартеньев.
Visual
⎯1⎯
Fortran:
новые
возможности
О. В. Бартеньев. Visual Fortran: новые возможности
Посо...
212 downloads
1370 Views
3MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
О.
В.
Бартеньев.
Visual
⎯1⎯
Fortran:
новые
возможности
О. В. Бартеньев. Visual Fortran: новые возможности
Пособие содержит обширный материал по специальным, расширяющим стандарт Фортрана возможностям Digital Visual Fortran, который, как известно, использует в том числе и все расширения Microsoft Fortran PowerStation 4.0. При изложении материала предполагалось, что, во-первых, читатель знаком с техникой программирования на Фортране и, во-вторых, имеет возможность работать с последними версиями Digital Visual Fortran или Microsoft Fortran PowerStation 4.0 для Интел-совместимых компьютеров. Использование рассмотренных в пособии методов и свойств Фортрана позволит читателю создавать быстро работающие, при необходимости многоязычные, приложения, имеющие удобный интерфейс и наглядно представляющие результаты вычислений. Предназначено для научно-технических работников, преподавателей, студентов и аспирантов вузов.
⎯2⎯
Предисловие В настоящее время на российском книжном рынке литература по Фортрану представлена достаточно широко [1, 2, 6 и 9]. Однако эти издания преимущественно отражают свойства Фортрана, определяемые стандартом 90-го года [1, 6 и 9], или посвящены раскрытию приемов программирования на современном Фортране [1 и 2]. Для создания многих приложений этого явно недостаточно: стандартом Фортрана не предусмотрены средства графического вывода, построения диалогов, создания многониточных программ, разноязычных приложений и др. Данная книга восполняет существующий пробел. Воспользовавшись приведенным в ней материалом, программист сможет организовать дружественный интерфейс (в виде диалоговых окон) между пользователем и выполняющими вычисления процедурами. Такие окна содержат управляющие элементы, поля для ввода и вывода данных. Окна Фортрана не являются окнами WIN32 API (хотя последние также могут быть в нем реализованы). Однако поводов для расстройства нет: диалоги Фортрана обеспечивают полноценный интерфейс для большинства его приложений. Одновременно с диалогами в проектах QuickWin поддерживается многооконный графический вывод, управляемый из программы, из диалогов, из сопровождающего графические окна меню или процедурами, запускаемыми при нажатии на заданные клавиши клавиатуры или кнопки мыши. Имеющиеся в Фортране графические процедуры позволяют отображать как векторные, так и растровые графические данные (включая в том числе и графический текст). Полагаю, что специалистам будет интересна глава, посвященная многониточному программированию, которое необходимо для организации квазипараллельного доступа к последовательным устройствам, например экрану или жесткому диску, и для моделирования нескольких одновременно протекающих процессов. В Фортране многониточное программирование реализовано, так же как и в других языках, например СИ++, процедурами WIN32 API. Часто незначительные изменения в программе могут существенно повысить ее быстродействие. Вопрос в том. какие изменения должны быть выполнены. Ответить на него помогут вам приведенные в главе "Повышение быстродействия программ" сведения. ⎯3⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Материал главы "Компилирование и построение программ" поможет вам научиться создавать приложения из командной строки. Возможно, эта идея не покажется вам привлекательной, однако приобретенные в результате чтения главы знания небесполезны и могут быть использованы при работе с проектом в среде Visual Studio как при его отладке, так и на этапе создания рабочей версии, которая должна быть компактной и быстрой. Есть ситуации, когда надо создать проект из процедур, написанных на Фортране, СИ и Ассемблере. Выход здесь прост, поскольку объектные коды, получаемые в этих языках, совместимы (если речь идет о продуктах Digital и Microsoft); дело за тем, чтобы договориться о правилах вызова разноязычных процедур и обмена данными. Такие договоренности есть, и о них рассказывается в главе "Программирование на нескольких языках". Завершают пособие 4 приложения. Несколько слов о двух последних. Известно, что DOS и Windows кодировки русских букв различаются. Это говорит о необходимости преобразования русских текстов при их передаче из Windows в DOS и обратно, например оператором PRINT или подпрограммой OUTTEXT, выполняющей вывод неграфического текста в приложениях с графикой. Процедуры таких преобразований приведены в прил. 3. Особо остановлюсь на последнем приложении. Оно выпадает из общей канвы книги и касается не специальных, а стандартных средств Фортрана: в нем изложены новые элементы языка, добавленные в него стандартом Фортран 95. Однако представляется, что такой материал необходим, поскольку в отечественных книжных магазинах до сих пор не появилось изданий, отражающих эту тему. Фортран, как и другие языки, встроен в современные операционные системы и имеет доступ ко всем их ресурсам. Это, в частности, видно из глав, посвященных QuickWin и многониточному программированию. Однако спектр этих средств существенно шире. Поэтому издательство “Диалог-МИФИ” планирует продолжить выпуск книг по Фортрану. В ближайшее время выйдет пособие по созданию в Фортране приложений с графикой OpenGL, а вслед - книга по разработке WIN32 API Фортранприложений.
⎯4⎯
1. Использование диалогов 1.1. Постановка задачи Рассмотрим программу, выполняющую табуляцию функции y = = xsinx на отрезке [a, b] с шагом dx. Исполняемый файл создадим как проект QuickWin. Напомним, что такой проект создается в среде MS Developer Visual Studio (VS) Digital Visual Fortran (DVF) 6.0 в результате выполнения цепочки: File - New - Projects - Fortran Standard Graphics or QuickWin Application - задать имя проекта (Project name) и папку его размещения (Location) - выбрать QuickWin (multiple windows) - Finish. program txy ! Программа табуляции функции y = x*sin(x) real(4) :: a, b, dx, x, y ! Объявление имен и типов переменных real(4), parameter :: dxmin = 0.05 ! Минимально допустимый шаг изменения x print *, 'Задайте границы отрезка a и b и шаг вычислений dx' print *, 'Левая граница: ' ! Выводим подсказку для пользователя read *, a ! Вводим с клавиатуры значение a print *, 'Правая граница: ' ! и нажимаем на Enter read *, b ! Так же вводятся и другие данные print *, 'Шаг вычислений: ' read *, dx if(dx < dxmin) stop 'Ошибка при задании шага dx' if(a > b) stop 'Ошибка при задании границ отрезка a и b (a > b)' print *, 'Зависимость y = x*sin(x): ' x=a do while(x b. Построение такого окна выполним в среде VS. Загрузим заготовку для диалога: Insert - Resource - выбрать Dialog для Resource type - New (или OK). В результате появится заготовка для окна с двумя кнопками - OK и Cancel.
1.2.2. Задание параметров диалога Настроим прежде параметры всего окна, щелкнув для этого 2 раза мышью по свободной поверхности окна. В появившемся окне Dialog Properties на вкладке General зададим: 1) в поле ID имя диалога - idd_txy, по которому он будет идентифицироваться в программе; 2) в поле Caption название диалога - “Исходные данные в задаче табуляции функции”, которое отображается на верхней полосе диалога; ⎯6⎯
1. Использование диалогов
3) установим, нажав на кнопку Font, шрифт, например MS Sans Serif, и его размер, например 10; 4) в полях XPos и YPos координаты верхнего левого угла диалога, которые он имеет в дочернем окне QuickWin, например 20 и 10 (координаты задаются в текстовых столбцах и рядах). Выберем затем вкладку Styles и уберем галочку с пункта System menu. Остальные поля этой и других вкладок оставим без изменений (рис. 1.2).
Рис. 1.2. Параметры диалогового окна
Закроем окно Dialog Properties, нажав, например, на клавишу Enter; увеличим привычным образом размеры диалогового окна; нажмем Ctrl+T и просмотрим получившееся окно. Нажмем Esc, с тем чтобы затем продолжить построение.
1.2.3. Задание и обработка статического текста Используя меню с управляющими элементами (рис. 1.3), зададим текстовые поля и поля для ввода и редактирования данных.
Рис. 1.3. Управляющие элементы
Задание текстового поля (Static text) выполняется после выбора управляющего элемента Aa и последующей прорисовки прямоугольника для предполагаемого текста в свободной области диалога. Прорисовка выполняется мышью при нажатой ее левой клавише. (Можно также после выбора элемента управления просто “ударить” мышью по предполагаемому его месту размещения в диалоге.) Выделим получившийся прямоугольник, ⎯7⎯
О. В. Бартеньев. Visual Fortran: новые возможности
ударив по нему мышью, и изменим его положение и размеры, пользуясь мышью или клавиатурой. Так, увеличить размер по оси x можно, нажимая Shift+→. “Ударим” теперь по тексту 2 раза мышью и зададим в окне Text Properties во вкладке General свойство Caption текста: “Задайте границы отрезка и шаг вычислений” (см. рис. 1.1). Остальные свойства оставим без изменений. Аналогичным образом зададим все иные тексты, кроме текста error, приведенные на рис. 1.1. Все тексты, поскольку они неизменяемы, будут иметь один и тот же задаваемый по умолчанию идентификатор: IDC_STATIC. Напомним, что для отмены действия можно использовать Ctrl+Z. Задаваемый подобным образом статический текст не может быть изменен в диалоге, но может быть изменен программно, чем мы и воспользуемся для вывода сообщений об ошибках. Создадим внизу диалога статический текст, задав его ID равным error, и внесем в поле Caption надпись error text (см. рис. 1.1). Кроме того, добьемся отображения границ поля, проставив галочки (√) во вкладке Styles рядом с пунктами Sunken и Border. Начальное значение текста с ошибкой зададим в подпрограмме rundial инициализации диалога (приведена в разд. 1.2.8) в виде пустой строки, применив функцию DLGSET: flag = dlgset(dxy, error, '')
! В случае удачи вернет .TRUE.
dxy - определяемая в программе переменная диалога, содержащая параметры окна. error - ID-имя элемента управления, последний параметр задает пустую строку. Функция DLGSET имеет логический тип и вернет .TRUE. в случае успеха. Переменная диалога dxy (имя переменной, разумеется, может быть произвольным) имеет тип dialog. (Тип определен в модуле DFLOGM в случае DVF и в модуле DIALOGM в случае FPS.) Значение переменной диалога определяется при его инициализации, выполняемой функцией DLGINIT. Например, для диалога с ID-именем idd_txy в приводимой ниже программе переменную диалога определим так: type(dialog) dxy flag = dlginit(idd_txy, dxy)
! dxy - переменная диалога idd_txy
Функция DLGINIT имеет логический тип и вернет .TRUE. в случае успеха. ⎯8⎯
1. Использование диалогов
Заметим, что примененная для статического текста функция DLGSET используется и для изменения значений и свойств других элементов управления. Вывод сообщения об ошибке в поле error диалога, когда dx < dxmin, выполним в программе так: flag = dlgset(dxy, error, 'Нужно увеличить шаг!') ! Ошибка: dx > dxmin
1.2.4. Обработка редактируемых полей Выберем контрольный элемент ab| и, применяя ту же технику, что и при создании статического текста, разместим в диалоге 3 редактируемых поля (Edit Box). Используем первое поле для ввода левой границы отрезка изменения аргумента x, второе - для ввода его правой границы, а третье для ввода шага изменения x. Присвоим полям имена: первому - aval, второму - bval и третьему - dxval. Имя поля задается в связанном с ним окне Edit Properties изменения свойств поля, которое появляется, если по полю дважды щелкнуть мышью. По умолчанию ID-имя первого поля имеет значение IDC_EDIT1, которое мы изменим на aval. Аналогичные операции повторим и для двух других редактируемых полей. Перемещение по редактируемым полям и другим элементам управления диалога выполняется при помощи мыши, клавиш Tab, Shift+Tab, Enter (Ввод и перемещение) и других клавиш. Редактируемые поля содержат символьные данные. Для передачи из программы в символьные поля числовых данных выполняются преобразования “число - строка”. При вводе числовых данных из символьного редактируемого поля диалога выполняются преобразования “строка - число”. Изменение значения поля при открытом диалоге можно выполнить из программы, применив функцию DLGSET. Например, после вызова flag = dlgset(dxy, aval, '-1.0') aval
! Изменяем из программы значение поля
в поле aval появится значение -1.0. Передача данных из поля в программу выполняется в результате вызова функции DLGGET. Например, вызов flag = dlgget(dxy, aval, string) aval
! Передаем в строку string значение поля
обеспечит передачу значения поля aval в символьную переменную string. Функция DLGGET имеет логический тип и вернет .TRUE. в случае успеха. ⎯9⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Напомним, что последующее преобразование “строка - число” выполняется так: read(string, *, iostat = ios) a
! Преобразование “строка - число”
Параметр ios равен нулю при успешном преобразовании. Если же строка string не может быть преобразована в вещественное число, например если она состоит из букв, то ios отличен от нуля. Преобразование “число - строка” можно выполнить, например, оператором write(string, *, iostat = ios) a
! Преобразование “число - строка”
С каждым редактируемым полем, например aval (и любым другим изменяемым элементом управления), может быть связана пользовательская подпрограмма обработки введенных данных, например indata, которая должна обладать атрибутом EXTERNAL. Эта связь устанавливается в результате применения функции DLGSETSUB, например: ! Связываем подпрограмму indata с элементом aval flag = dlgsetsub(dxy, aval, indata)
Подпрограмма обработки мгновенно вызывается, если изменено значение редактируемого поля, и должна иметь следующий интерфейс: subroutine indata(dlg, control_name, callbacktype)
То есть подпрограмма обработки всегда в качестве входных данных получает значение переменной диалога, ID-имя элемента управления (одна и та же подпрограмма может быть связана с разными элементами управления) и вид воздействия на элемент управления, например DLG_CLICKED - удар мышью, DLG_CHANGE - изменение значения элемента управления или DLG_DBLCLICK - двойной удар мышью. Элементы, воспринимающие несколько воздействий, могут быть связаны с разными подпрограммами. Свяжем подпрограмму indata с полями диалога aval, bval и dxval и используем ее для ввода данных диалога, выявления возможных ошибок и выдачи соответствующих сообщений в поле error диалога idd_txy.
1.2.5. Кнопки OK и Cancel Кнопки OK и Cancel появляются в каждом вновь создаваемом диалоге, и с ними связаны задаваемые по умолчанию процедуры. Можно изменить как ID-имена этих кнопок, так и их названия. Разумеется, каждая из этих кнопок может быть удалена из диалога. Изменим устанавливаемые по умолчанию ID-имена кнопок, задав для кнопки OK вместо имени IDOK ID⎯ 10 ⎯
1. Использование диалогов
имя yes, а для кнопки Cancel вместо имени IDCANCEL зададим ID-имя no. Все это выполним по той же схеме, что и для рассмотренных выше элементов управления. Свяжем с кнопками OK и Cancel подпрограмму what, которая при нажатии на OK: •
проверяет корректность задания данных и анализирует возможные ошибки;
•
выводит в поле error диалога сообщения об ошибках;
•
присваивает переменной flag значение .TRUE., если введенные данные не содержат ошибок, и закрывает диалог. Подпрограмма what присваивает переменной flag значение .FALSE. и закрывает диалог, если нажата кнопка Cancel. Связь кнопок с подпрограммой выполним так: flag = dlgsetsub(dxy, yes, what) flag = dlgsetsub(dxy, no, what) Cancel
! Свяжем подпрограмму what с кнопкой OK ! Свяжем подпрограмму what с кнопкой
1.2.6. Меню диалога Помимо приведенного на рис. 1.3 меню с управляющими элементами (Controls) при работе с диалогом можно пользоваться меню Dialog (рис. 1.4).
Рис. 1.4. Меню Dialog
Это меню позволяет изменять положение и геометрию выбранных полей диалога, задавать сетку в проектируемом окне, линейку измерения его размеров, просматривать (Ctrl+T) диалог. Если выбрано одно поле, то его можно отцентрировать как по вертикали (Ctrl+F9), так и по горизонтали (Ctrl+Shift+F9). Если выбраны несколько полей, то они могут быть: •
выравнены влево (Align Left - Ctrl+←);
•
выравнены вправо (Align Right - Ctrl+→);
•
выравнены вверх (Align Top - Ctrl+↑);
•
выравнены вправо (Align Bottom - Ctrl+↓); ⎯ 11 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
•
отцентрированы по вертикали и горизонтали;
•
сделаны одной ширины (Make Same Width);
•
сделаны одной высоты (Make Same Height);
•
сделаны одного размера (Make Same Size).
Меню диалога можно убрать, нажав, например, на крест (×) (рис. 1.4). При необходимости его впоследствии можно восстановить, найдя в главном меню пункт Toolbars и поставив галочку (√) после выбора Toolbars рядом с пунктом Dialog. Аналогичным образом можно активизировать и ранее закрытое меню Controls.
1.2.7. Доступ к файлам хранения диалога При сохранении диалога создается файл ресурсов, которому мы присвоим имя xyd.rc и который расположим в папке, содержащей исходный код. При сохранении диалога в папке с файлом xyd.rc появляются или обновляются файлы resource.h и resource.fd, содержащие параметры диалога и других ресурсов. Файл ресурсов может содержать диалоги, меню и другие ресурсы, однако пока в нем будет только один диалог - idd_txy. Файл xyd.rc, чтобы программа получила к нему доступ, необходимо вставить в проект. Выполним в FPS цепочку: Insert - File into Project - задать Resource files для типа файлов - выбрать файл xyd.rc - Add. В DVF 6.0 файл ресурсов вставляется в специальную папку Resource Files окна File View: выбрав эту папку, ударим по правой клавише мыши, выберем Add Files to Folder и добавим xyd.rc в папку. Выполним компиляцию проекта. В среде MS Developer Visual Studio (VS) - в окне работы с проектом (View window) - появится вкладка Resource view, переместившись в которую можно просмотреть и состав файла ресурсов xyd.rc, и отдельно каждый из его компонентов. Существующее диалоговое окно будет загружено в среду VS, если дважды щелкнуть мышью по его ID-имени, отображаемому на вкладке Resource view. Появившееся окно доступно для редактирования. На вкладке File View после компиляции проекта появится подраздел Dependencies, содержащий ссылку на файл resource.fd. Отметим, что пользователь должен воздержаться от ручного редактирования этого файла.
⎯ 12 ⎯
1. Использование диалогов
1.2.8. Работа с диалогом в программе Программу реализуем по следующей схеме: •
выполним инициализацию (подпрограмма rundial);
•
отобразим, обратившись к функции DLGMODAL, диалог на экране;
•
введем данные и выполним их проверку на предмет наличия ошибок (подпрограмма indata);
•
нажмем OK или Cancel;
•
при выборе OK выполним дополнительные проверки введенных данных (см. подпрограмму what) и при отсутствии ошибок, закрыв диалог, перейдем к вычислениям; при наличии ошибок выведем соответствующие сообщения;
диалога
и
его
отдельных
полей
•
при выборе Cancel закроем диалог и завершим программу. Программа устроена так, что при наличии ошибок в данных кнопка OK диалога станет неактивной и, следовательно, не может быть нажата. Факты наличия ошибок фиксируются массивом is_ok(1:5). Следующее ветвление переводит кнопку OK в активное или неактивное состояние: if(all(if_ok)) then ! Если все элементы is_ok равны .TRUE. flag = dlgset(dlg, yes, .true., dlg_enable) ! Ошибок в данных нет - кнопка OK активна else ! Если есть хотя бы одна ошибка flag = dlgset(dlg, yes, .false. , dlg_enable) ! в данных,то кнопка OK недоступна
Отметим, что в приведенных вызовах функции DLGSET индекс DLG_ENABLE может быть опущен. Текст программы: ! Выполним обмен данными между программными компонентами, ! используя use-ассоциирование module abdx ! Обязательная ссылка при работе с диалогами на модуль DIALOGM в случае FPS ! или DFLOGM в случае DVF use dialogm ! Обязательное включение файла с параметрами ресурсов (с параметрами диалога) include 'resource.fd' ! Тип dialog определен в модуле DIALOGM (FPS) или DFLOGM (DVF) type(dialog) :: dxy ! Задаем переменную диалога real(4) :: a, b, dx ! Отрезок и шаг вычислений real(4), parameter :: dxmin = 0.05 ! Минимально допустимый шаг
⎯ 13 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
logical(4) :: flag, if_ok(5) integer(4) :: status, ios character(50) :: string end module abdx
! Промежуточные данные
program txy ! Главная программа use abdx real(4) :: x, y call rundial( ) ! Инициализация диалога idd_txy if(flag) then ! Если в диалоге нажата кнопка OK и нет ошибок x=a ! Подготовка к вычислениям do while(x = xf) then call beepqq(200, 5) color = color2; dx = - dx else if(x = xf) then call beepqq(200, 5); color = color2; dx = - dx else if(x 255) blueval = 100 color1 = rgbtointeger(0, blueval, blueval) color2 = rgbtointeger(100, 100, 100) ! Темно-серый цвет
⎯ 155 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
y = ys; xf = xs + blen; x = xs; dx = 5; color = color1 do call EnterCriticalSection(loc(rts)) if(hthread(2, barcount) == 0) then ! Если полоса barcount закрыта call LeaveCriticalSection(loc(rts)) exit end if status4 = setactiveqq(barcount) ! Направим вывод на окно с номером barcount status4 = setcolorrgb(color) status2 = rectangle($gfillinterior, x, y, x + dx, y + h) call LeaveCriticalSection(loc(rts)) x = x + dx if(x >= xf) then call beepqq(200, 5); color = color2; dx = - dx else if(x 0) status4 = clickmenuqq(qwin$tile) end if call LeaveCriticalSection(loc(rts)) if(n_of_bars == 0) call exit(0) ! Останов программы, если закрыты все окна return; call unusedqq(fl) end subroutine deletebar subroutine exitbar(fl) logical(4) :: fl lret = CloseHandle(obj2) call exit(0)
⎯ 156 ⎯
4. Многониточное программирование
return; call unusedqq(fl) end subroutine exitbar end module bar_data2 program bars7 use bar_data2 call InitializeCriticalSection(loc(rts)) obj2 = CreateMutex(0, .true., 0) n_of_bars = 0 call createbar(.false.) call createbar(.false.) call sleepqq(1000000) end program bars7 программы
! Число созданных полос ! Создадим две нити ! Задержка, необходимая для того, чтобы ! не произошла мгновенная остановка
function initialsettings( ) ! Формируем меню, управляющее окнами вывода use bar_data2 logical(4) :: initialsettings ! Создаем пункты меню lret = appendmenuqq(1, $menuenabled, '&Выход'c, exitbar) lret = appendmenuqq(2, $menuenabled, '&Создать полосу'c, createbar) lret = appendmenuqq(3, $menuenabled, '&Удалить полосу'c, deletebar) lret = appendmenuqq(4, $menuenabled, '&Окна'c, nul) lret = appendmenuqq(4, $menuenabled, '&Каскад'c, wincascade) lret = appendmenuqq(4, $menuenabled, '&Расположить все'c, wintile) lret = setwindowmenuqq(4) ! Теперь 4-й пункт меню будет содержать initialsettings = .true. ! список всех дочерних окон end function initialsettings
Пояснение. В программе bars7 использованы функции QuickWin: •
SETWSIZEQQ - задает позицию обрамляющего или дочернего окна;
•
CLICKMENUQQ - имитирует выполнение команды меню;
•
SETACTIVEQQ - делает активным дочернее окно (без размещения его в фокусе);
•
INQFOCUSQQ- определяет, какое окно находится в фокусе;
•
APPENDMENUQQ - добавляет пункт меню в конец меню и фиксирует процедуру, выполняемую при выборе данного пункта меню;
•
SETWINDOWMENUQQ - задает пункт меню, в котором отображается список открытых окон;
•
INITIALSETTINGS - инициализация QuickWin.
⎯ 157 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
4.6. Перечень многониточных процедур Список используемых при многониточном программировании процедур приведен в табл. 4.2. Часть процедур описана в настоящем разделе. Синтаксис не рассмотренных в настоящем разделе процедур можно найти в поставляемом с Фортраном руководстве программиста и документации по Win32 API. Таблица 4.2. Процедуры многониточного программирования Процедура
Тип
Назначение
CloseHandle
LOGICAL(4)
Закрывает обработчик объекта или нити
CreateEvent
INTEGER(4)
Создает объект-событие
CreateMutex
”
Создает объект-исключение
CreateProcess
LOGICAL(4)
Создает процесс
CreateSemaphore
INTEGER(4)
Создает объект-семафор
CreateThread
”
Создает нить
DeleteCriticalSection
Подпрограмма
Удаление критической секции
DuplicateHandle
LOGICAL(4)
Дублирует обработчик объекта
EnterCriticalSection
Подпрограмма
Находит владельца критической секции
ExitProcess
Подпрограмма
Выход из процесса
ExitThread GetCurrentProcess
”
Выход из процедуры-нити
INTEGER(4)
Возвращает значение обработчика текущего процесса
GetCurrentProcessId
”
Возвращает значение идентификатора текущего процесса
GetCurrentThread
”
Возвращает значение обработчика исполняемой нити
GetCurrentThreadId
”
Возвращает значение идентификатора исполняемой нити
GetExitCodeProcess
LOGICAL(4)
Получает значение статуса завершения указанного процесса
⎯ 158 ⎯
4. Многониточное программирование
GetExitCodeThread GetLastError
”
Получает значение статуса завершения указанной нити
INTEGER(4)
Возвращает код ошибки вызываемой нити
GetPriorityClass
”
Возвращает класс приоритета заданного процесса
GetThreadPriority
”
Возвращает приоритет заданной нити
InitializeCriticalSection
LeaveCriticalSection OpenEvent
Подпрограмма ”
Инициализация критической секции Освобождение критической секции от нити-владельца
INTEGER(4)
Возвращает значение обработчика существующего именованного объекта-события
OpenMutex
”
Возвращает значение обработчика существующего именованного объекта-исключения
OpenProcess
”
Возвращает значение обработчика существующего именованного объекта-процесса
OpenSemaphore
INTEGER(4)
Возвращает значение обработчика существующего именованного объекта-семафора
PulseEvent
LOGICAL(4)
Устанавливает объект-событие в состояние “подать сигнал” и после запуска находящихся в состоянии ожидания нитей переводит объектсобытие в состояние “не подавать сигнал”
ReleaseMutex
”
Освобождает объект-исключение от нити, владеющей этим объектом
ReleaseSemaphore
”
Освобождает объект-семафор от нити, владеющей этим объектом
ResetEvent
”
Устанавливает объект-событие в состояние “не подавать сигнал”
⎯ 159 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
ResumeThread
INTEGER(4)
Возобновляет функционирование нити
SetEvent
LOGICAL(4)
Устанавливает объект-событие в состояние “подать сигнал”
SetLastError
Подпрограмма
Устанавливает код последней ошибки для вызываемой нити
SetPriorityClass
LOGICAL(4)
Устанавливает класс приоритета заданного процесса
SetThreadPriority
”
Устанавливает значение приоритета заданной нити
SuspendThread
INTEGER(4)
Приостанавливает функционирование нити
TerminateProcess
LOGICAL(4)
Завершает заданный процесс и все его нити
TerminateThread
Подпрограмма
Завершает заданную нить
WaitForMultipleObjects
INTEGER(4)
Возвращает значение, когда один или все заданные объекты находятся в состоянии "подать сигнал" или когда закончился заданный промежуток времени
WaitForSingleObject
”
Возвращает значение, когда заданный объект находятся в состоянии "подать сигнал" или когда закончился заданный промежуток времени
⎯ 160 ⎯
5. Компиляция и построение программ 5.1. Назначение команды DF Программа может быть построена либо в среде Microsoft Visual Studio (VS), либо в результате использования команды DF или команд DF и LINK, которые вызываются из командной строки. Предметом дальнейшего рассмотрения будет команда DF. По существу эта команда запускает программу-драйвер, которая: • принимает имена файлов и опции, управляющие компиляцией и построением программы; • вызывает компилятор DVF и передает ему предназначенные для компиляции опции и имена компилируемых файлов; • передает построителю предназначенные для него опции и имена объектных, созданных компилятором, файлов; • передает построителю имена библиотек объектных файлов. Каждая из запускаемых командой DF программ (компилятор или построитель) либо генерируют результат, выдавая при необходимости те или иные предупреждения, либо генерируют сообщения о найденных ошибках, без исправления которых нельзя выполнить компиляцию и/или построение программы. Построитель исполняемых файлов может быть также запущен из командной строки и командой LINK. Перечень опций команды DF и их синтаксис можно просмотреть, задав в командной строке DF /? или DF /help. Аналогично осуществляется просмотр опций команды LINK.
5.2. Переменные окружения Если DVF установлен стандартным образом, например в директорию DVF5, то для запуска команды DF необходимо задать переменным окружения TMP или TEMP, PATH, INCLUDE и LIB следующие значения: set temp=c:\temp set tmp=c:\temp set path=d:\dvf5\shared~1\bin;d:\dvf5\vc\bin;d:\dvf5\df\bin;"%path%" set include=d:\dvf5\vc\include;d:\dvf5\df\include;"%include%" set lib=d:\dvf5\vc\lib;d:\dvf5\df\lib;"%lib%"
⎯ 161 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Переменные окружения PATH, INCLUDE и LIB можно занести в файл autoexec.bat при установке DVF. При необходимости окружение можно установить, выполнив поставляемый с DVF файл dfvars.bat. Кроме этих переменных окружения для хранения часто применяемых опций можно использовать переменную окружения, имеющую имя DF. Описание переменных окружения приведено в табл. 5.1. Таблица 5.1. Переменные окружения, используемые командой DF Переменная
Описание
PATH
Задает пути поиска EXE и других файлов
INCLUDE
Переменная используется командой NMAKE, компилятором исходных файлов и компилятором ресурсов. NMAKE и компилятор исходных файлов используют переменную INCLUDE для поиска INCLUDE-файлов, включаемых оператором INCLUDE, и модулей, на которые ссылается оператор USE. Компилятор ресурсов, например диалоговых окон, использует переменную INCLUDE для обнаружения #include и RCINCLUDE файлов
LIB
Построитель использует эту переменную для поиска LIBфайлов, содержащих объектные библиотеки. Их поиск также выполняется и в текущей директории, т. е. директории, из которой выполнен запуск команды DF или LINK. Если эта переменная не задана, то поиск LIB-файлов выполняется только в текущей директории
DF
Используется для хранения часто применяемых опций. Опции и файлы, заданные переменной окружения DF, добавляются в команду DF и обрабатываются командой до первой явно заданной в ней опции. Однако действие опции, заданной в переменной DF, можно подавить путем задания в командной строке одноименной опции с другими значениями параметров
TMP
Задает директорию, в которую записываются создаваемые компилятором и построителем временные файлы
TEMP
Компилятор и построитель заносят временные файлы в директорию, заданную переменной TEMP, если не определена переменная окружения TMP
⎯ 162 ⎯
5. Компиляция и построение программ
5.3. Формат команды DF Команда DF принимает опции компилятора и построителя и имена как входных, так и выходных файлов. Порядок следования опций в команде DF определяется правилами: • опции компилятора могут следовать в произвольном порядке; • опциям построителя, если они задаются, должно предшествовать ключевое слово /link. Сами же опции построителя располагаются в конце командной строки вслед за другими опциями. Общий вид команды DF: DF options [/link options] Параметр options задает опции компилятора или построителя; имеет в общем случает следующий формат: [/option[:arg]] [filenamme.ext] /option[:arg] задает либо действия, которые должны быть выполнены компилятором или построителем, либо специальные свойства входных или выходных файлов. Некоторые опции могут принимать в качестве arg несколько ключевых слов. Синтаксис, используемый при наличии одного ключевого слова: DF /warn:unused test.f90 В случае нескольких ключевых слов они помещаются в скобки и разделяются запятыми: DF /warn:(argument_checking, unused) test.f90 Пробелы между двоеточием и опцией и двоеточием и ключевым словом или списком ключевых слов недопустимы. Возможен альтернативный синтаксис команды, в котором вместо двоеточия используется знак равенства: DF /warn=(argument_checking, unused) test.f90 filenamme.ext - одно или несколько имен входных или выходных файлов. Если в текущей директории обработке подлежат все файлы, например исходные, то в команде DF достаточно задать *.f90. Перед именем файла, если он не может быть найден командой DF, должен быть указан ведущий к нему путь. Расширение файла задает тип файла и определяет, какой программе, компилятору или построителю будет передан тот или иной файл.
⎯ 163 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
5.4. Правила задания опций Опции компилятора и построителя задаются вслед за слешем (/). Написание опций компилятора может быть сокращенным, например вместо /alignment:commons допустима запись /align:commons. Опции построителя сокращать нельзя. Пробелы или знаки табуляции недопустимы при написании опции и ее параметров. Имена опций и их параметров нечувствительны к регистру. Построитель прежде использует опции, заданные переменной окружения LINK. Затем он использует опции, заданные в командной строке, в порядке их следования. Если опция повторяется с различными параметрами, то приоритет имеет последняя опция. Опции построителя применяются ко всему приложению и не могут быть заданы для отдельных входных файлов.
5.5. Входные и выходные файлы В качестве входных файлов команда DF обрабатывает исходные и объектные файлы; последние могут размещаться в библиотеках - LIBфайлах. Входные файлы передаются компилятору, если они имеют одно из следующих расширений: .f90, .for, .f, .fpp, .i, .i90, .inc, .h, .c или .cpp. Типичными расширениями исходных файлов Фортрана являются .f90, .for и .f. Входные файлы поступают построителю, если они снабжены расширением .lib, .obj, .o, .exe, .res, .rbj или .def. Обычно объектные файлы имеют расширение .obj, а библиотеки объектных файлов - .lib. Команда DF вырабатывает следующие выходные файлы: •
файл с исходным текстом (.fpp), если задана опция компилятора /keep;
•
объектный файл (.obj), если заданы опции /compile_only, или /keep, или /object;
•
файл динамически подключаемой библиотеки (.dll), если задана опция /dll и не опущена опция /compile_only;
•
файл модуля (.mod), если компилируется модуль, заданный оператором MODULE;
•
база данных программы (.pdb), если заданы опции /pdbfile или /debug:full;
•
листинг (.lst), если задана опция /list; ⎯ 164 ⎯
5. Компиляция и построение программ
•
файл просмотра (.sbr), если задана опция /browser. Команда DF, если не заданы опции /compile_only или /keep, генерирует один временный объектный файл из одного или более исходных файлов. Затем построитель создает из этого объектного файла один исполняемый EXE-файл.
5.6. Формирование имен выходных файлов •
Имя исполняемого EXE-файла: определяется именем исходного файла, если использована команда DF test.f90 После выполнения этой команды, если нет ошибок, сформируется исполняемый файл test.exe; объектный файл test.obj не создается. Команда DF test1.f90 test2.f90 test3.for создаст исполняемый test1.exe, определив его имя по имени первого входного файла;
•
задается опцией /exe команды DF: DF test.f90 /exe:result.exe
•
задается опцией /out построителя: DF test.f90 /link /out:result.exe Имена объектных файлов:
•
определяются именами исходных файлов, если задана опция /compile_only или /keep: DF /compile_only find_ab.f90 find_cd.f90 После выполнения этой команды, если нет ошибок, сформируются объектные файлы find_ab.obj и find_cd.obj;
•
задаются опцией /object команды DF: DF /compile_only find_ab.f90 find_cd.f90 /object:result.obj Объектные файлы не будут созданы, если в командной строке отсутствуют опции /compile_only и /keep.
5.7. Временные файлы Временные файлы создаются и компилятором и построителем. Они записываются в директорию, заданную переменной окружения TMP. Если ⎯ 165 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
такая переменная не определена, то временные файлы запишутся в директорию, которая задана переменной окружения TEMP. Если же не определена и эта переменная, то для временных файлов будет использована текущая директория. Обычно DVF уничтожает временные файлы. Однако этого не произойдет, если задана опция /keep. Чтобы узнать, куда записываются временные файлы, используйте опцию /verbose, например: DF test.f90 /verbose
5.8. Управление библиотекой объектных файлов Статической библиотекой объектных файлов можно управлять как из среды VS, так и командой LIB, запускаемой из командной строки. В среде VS для доступа к библиотеке необходимо, определяя тип нового проекта, выбрать Static Library. Библиотека объектных файлов называется статической потому, что содержащиеся в ней программные компоненты могут присоединяться к исполняемому файлу только при его построении. Программные единицы динамических библиотек (DLL), в отличие от статических, вызываются EXE-файлом в момент его исполнения. Вспомним некоторые приемы работы с пользовательской библиотекой объектных файлов в командной строке. Объектные файлы b.obj и c.obj будут добавлены в находящуюся в текущей директории библиотеку stalib.lib после выполнения команды LIB stalib.lib b.obj c.obj Если библиотеки stalib.lib не существует, то ее следует создать, применив команду LIB /out:stalib.lib b.obj c.obj после выполнения которой библиотека не только будет создана, но в нее попадут файлы b.obj и c.obj. Теперь в библиотеку stalib.lib можно добавлять другие объектные файлы. После включения в библиотеку объектные файлы следует с диска удалить. Содержимое библиотеки будет выведено на экран монитора после выполнения команды LIB /list stalib.lib После запуска команды ⎯ 166 ⎯
5. Компиляция и построение программ
LIB /list:info.txt stalib.lib состав библиотеки отобразится в файле info.txt. Объектный файл, например b.obj, извлекается из библиотеки в текущую директорию командой LIB /extract:b.obj stalib.lib Извлекаемый файл в библиотеке, разумеется, сохраняется. Команда LIB /remove:b.obj stalib.lib удалит объектный файл b.obj из библиотеки stalib.lib.
5.9. Варианты использования команды DF 5.9.1. Компиляция и построение с одним исходным файлом Пусть исходные тексты модулей, главной программы и процедуры размещены в файле example.f90. Различные варианты использования команды DF для этого случая опишем в табл. 5.2. Таблица 5.2. Результаты применения команды DF к одному файлу с исходным текстом Команда
Выходные файлы
DF example.f90 /compile_only
example.obj
DF example.f90 /compile_only /object:result1
result1.obj
DF example.f90
example.exe, example.pdb
DF example.f90 /object:example.obj
example.obj, example.exe, example.pdb
DF example.f90 /object
То же
DF example.f90 /object:result1
result1.obj, example.exe, example.pdb
DF example.f90 /exe:result1.exe
result1.exe, result1.pdb
DF example.f90 /link /out:result1.exe DF example.f90 /object:example /exe:result1.exe
"
“
example.obj, result1.exe, result1.pdb
DF example.f90 /object: /exe:result1
"
⎯ 167 ⎯
“
“
О. В. Бартеньев. Visual Fortran: новые возможности
DF example.f90 /object:result1 /exe:result1
result1.obj, result1.exe, result1.pdb
5.9.2. Применение переменной окружения DF Составим файл, например e.bat, в котором определим переменную окружения DF и зададим одноименную команду set DF=/debug:minimal /list DF example.f90 Тогда после запуска файла e.bat команда DF воспримет все заданные в переменной DF опции и, в частности, выдаст листинг в файле example.lst. Запустим тот же файл, прежде добавив в команду DF опцию /show:code, включающую в листинг машинный код программы. set DF=/debug:minimal /list DF example.f90 /show:code В третьем варианте файла e.bat показано, как действие опции /list:out2 команды DF перекрывает действие опции /list, заданной переменной окружения DF: set DF=/debug:minimal /list DF example.f90 /show:code /list:out2 После запуска файла e.bat файл листинга будет иметь имя out2.lst.
5.9.3. Компиляция и построение с несколькими исходными файлами Пусть в текущей директории находится 3 файла с исходным текстом a.f90, b.f90 и c.f90. Выходные файлы, создаваемые командой DF, когда она принимает несколько исходных файлов, опишем в табл. 5.3. Таблица 5.3. Результаты применения команды DF к исходным файлам Команда
Выходные файлы
DF a.f90 b.f90 c.f90 /compile_only
a.obj, b.obj, c.obj
DF *.f90 /compile_only
"
“
DF a.f90 b.f90 c.f90 /compile_only /object:abc
abc.obj
DF a.f90 b.f90 c.f90
a.exe, a.pdb
DF *.f90
"
DF modfile.f90 a.f90 b.f90 c.f90
“
“
mod2.mod, modfile.exe, modfile.pdb
⎯ 168 ⎯
5. Компиляция и построение программ
DF modfile.f90 a.f90 b.f90 c.f90 /exe:result.exe DF modfile.f90 a.f90 b.f90 c.f90 /link /out:result.exe
mod2.mod, result.exe, result.pdb "
“
“
Если компилируется несколько файлов и применяется опция /compile_only, то обязательно задавайте опцию /object с тем, чтобы компилятор в результате совместного анализа обрабатываемых файлов смог создать более оптимальный по производительности код. Если программа содержит тексты определенных оператором MODULE модулей и вы используете несколько команд DF, то прежде компилируются файлы с модулями, а затем программы, эти модули использующие. Когда же в подобной ситуации используется одна команда DF, то, задавая в ней файлы с исходным текстом, прежде размещайте имена файлов с модулями, а вслед - имена файлов с другими программными компонентами, например: DF modfile.f90 a.f90 b.f90 c.f90 Результат обработки набора команд приведен в третьей снизу строке табл. 5.3. Файл mod2.mod появится в текущей директории, если в файле modfile.f90 определен модуль mod2. Имя генерируемого исполняемого файла можно контролировать, что отражено в двух последних строках табл. 5.3.
5.9.4. Использование последовательности команд Пусть вновь в текущей директории размещено 3 файла с исходным текстом a.f90, b.f90 и c.f90. Получим объектный и исполняемый файлы, выполнив последовательность DF a.f90 b.f90 c.f90 /list:result.lst /compile_only /object:result.obj DF result.obj Первая команда выполняет также и генерацию листинга result.lst исходных файлов. Это обеспечивается опцией /list:result.lst. Вторая команда вызывает построитель, который, получая файл result.obj, создает файл result.exe. Исполняемый файл можно получить и выполнив несколько иные команды: DF a.f90 b.f90 c.f90 /list: /compile_only DF a.obj b.obj c.obj
⎯ 169 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Теперь первая команда создаст 3 файла с листингами a.lst b.lst c.lst и 3 файла с объектным кодом a.obj b.obj c.obj. Вторая команда, получив в качестве входных объектные файлы, сгенерирует файлы a.exe и a.pdb.
5.9.5. Подключение библиотек объектных файлов Построение исполняемого файла a.exe из одного исходного файла и файлов, содержащихся в статической объектной библиотеке stalib.lib, обеспечит команда DF a.f90 stalib.lib При необходимости можно подключить и несколько пользовательских библиотек, задавая привычным образом их имена в команде DF: DF a.f90 stalib.lib mylib.lib /exe:result.exe
5.9.6. Использование динамических библиотек Динамическую библиотеку можно создать как в среде VS, так и командой DF, запуская ее из командной строки. В среде VS для доступа к библиотеке необходимо, определяя тип нового проекта, выбрать DynamicLink Library. Код, включенный в динамическую библиотеку, становится доступным приложению во время его исполнения. Однако для этого в приложение компилятор должен занести соответствующие ссылки на DLL. Рассмотрим код главной программы simple, находящейся в файле a.f90: program simple real(4) a, b, c, d integer(4) :: k a = 10; b = 20; c = 30; d = 40; k = 5 call find_ab(a, b) call find_cd(c, d) print '(4f7.2, i4)', a, b, c, d, k read * end program simple
! Код подпрограмм find_ab и find_cd ! размещен в DLL
! 1.00 2.00 3.00 4.00 5 ! Ожидаем нажатия Enter
Разместим вызываемые из программы simple подпрограммы find_ab и find_cd соответственно в файлах b.f90 и c.f90. Запишем затем их коды в динамическую библиотеку test.dll. Для этого снабдим эти подпрограммы атрибутом DLLEXPORT, указывающим на то, что код экспортируется в DLL, и задающим имя экспортируемого символа. subroutine find_ab(a, b) !dec$attributes dllexport :: find_ab real(4) :: a, b
⎯ 170 ⎯
5. Компиляция и построение программ
a = 1.0; b = 2.0 end subroutine find_ab subroutine find_cd(c, d) !dec$attributes dllexport :: find_cd real(4) :: c, d c = 3.0; d = 4.0 end subroutine find_cd
Создание динамической библиотеки test.dll. выполним командой DF b.f90 c.f90 /dll /exe:test.dll в которой опции /libs:dll и /dll задают режим формирования однониточной динамической библиотеки, а опция /exe определяет ее имя. При отсутствии последней опции библиотека будет иметь имя b.dll, т. е. ее имя определится по имени первого компилируемого файла. Помимо файла test.dll, компилятор сформирует файлы test.pdb, test.exp и test.lib. Последний файл test.lib, используем в команде DF, формирующей исполняемый файл a.exe: DF a.f90 /libs:dll test.lib Опция /libs:dll указывает на то, что не найденные построителем процедуры должны быть взяты из динамических библиотек, а файл test.lib предоставляет компилятору код, необходимый для организации ссылок на DLL. Теперь все готово для запуска a.exe. Однако следует помнить, что если файл a.exe переносится в другую директорию, то вместе с ним должен быть перенесен и файл test.dll. В противном случае динамическая библиотека найдена не будет, возникнет ошибка выполнения и программа прекратит работу. Заметим, что использование DLL приводит к снижению размера кода приложения.
5.9.7. Компиляция и построение приложений с текстами программ на Фортране и СИ Рассмотрим 3 файла: a.f90, d.f90 и cfun.c. Первый и второй файлы содержат Фортран-программы, а третий - СИ-функцию. Состав файла a.f90: program simple interface subroutine find_ab(a, b) ! Интерфейс СИ-функции find_ab !dec$attributes stdcall, alias : '_find_ab@8' :: find_ab
⎯ 171 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
integer(4) :: a, b программе end subroutine find_ab end interface real(4) :: a, b, c, d integer(4) :: k a = 10; b = 20; c = 30; d = 40; k = 5 call find_ab(loc(a), loc(b)) call find_cd(c, d) print '(4f7.2, i4)', a, b, c, d, k end program simple
! a, b - адреса объявленных в главной ! вещественных переменных a, b
! Вызов СИ-функции find_ab ! Вызов подпрограммы Фортрана find_cd
Состав файла d.f90: subroutine find_cd(c, d) real(4) :: c, d c = 3.0; d = 4.0 end subroutine find_cd
Состав файла cfun.c: void __stdcall find_ab(float *a, float *b) { *a = 1.0; *b = 2.0; }
При совместном использовании разноязычных текстов необходимо определиться с соглашениями о вызовах. Используем соглашение STDCALL. Информация об используемом соглашении сообщается СИ-функции в ее заголовке: void __stdcall find_ab(float *a, float *b)
В главной программе simple, из которой вызывается СИ-функция find_ab, определим ее интерфейс, указав в нем как тип используемого соглашения, так и имя _find_ab@8, которое СИ-функция find_ab получит при компиляции. Используем для этих целей атрибуты STDCALL и ALIAS: interface ! Интерфейс СИ-функции find_ab subroutine find_ab(a, b) ! Определим псевдоним функции и тип соглашения !dec$attributes stdcall, alias : '_find_ab@8' :: find_ab integer(4) :: a, b ! a, b - адреса объявленных в главной программе end subroutine find_ab ! вещественных переменных a, b end interface
⎯ 172 ⎯
5. Компиляция и построение программ
Имя _find_ab@8 является вторым именем (псевдонимом) СИ-функции find_ab. Число 8, завершающее имя, говорит о том, что формальные параметры функции find_ab занимают 8-байтовый отрезок памяти. Поскольку формальными параметрами функции являются указатели, то при ее вызове из Фортран-программы в качестве фактических параметров следует использовать адреса соответствующих переменных, которые возвращаются встроенной функцией LOC и имеют тип INTEGER(4). Это обстоятельство учтено и при написании интерфейса функции find_ab, и при ее вызове: call find_ab(loc(a), loc(b))
! Вызов СИ-функции find_ab
Компиляцию СИ-функции выполним командой CL /c cfun.c Ее результатом будет файл cfun.obj. Компиляцию и построение исполняемого файла, обеспечивающего выполнение программы simple, осуществим командой DF a.f90 d.f90 cfun.obj /debug:none Результатом ее выполнения станет исполняемый файл a.exe. Чтобы выполнить все приведенные команды, переменные окружения должны обеспечить доступ как к вызываемым командам (CL и DF), так и ко всем необходимым для их выполнения библиотекам. В системе, использующей различные версии компиляторов, возможен такой вариант задания переменных окружения: set path=c:\msdev\bin;d:\dvf5\df\bin;d:\progs\vs\msdev98\bin;d:\progs\vc98\bin;"%path%" set include=d:\dvf5\df\include;d:\progs\vc98\include;"%include%" set lib=d:\dvf5\vc\lib;d:\dvf5\df\lib;d:\progs\vc98\lib;"%lib%"
Рассмотрим теперь случай, когда главная программа написана на СИ. Пусть по-прежнему из главной программы вызываются две процедуры: find_ab и find_cd (в СИ есть только один вид процедур - функции). Первую реализуем в виде функции СИ и запишем в файл cfun.c, а вторую составим на Фортране и разместим в файле d.f90. Главную программу, функцию main, поместим в файл cmain.c: #include <stdio.h> void main( ) { float a, b, c, d; int k; void find_ab(float *a, float *b); find_cd
// Опишем прототипы функций find_ab и
⎯ 173 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
extern void find_cd(float *c, float *d); a = 10; b = 20; c = 30; d = 40; k = 5; find_ab(&a, &b); // Передаем параметры a и b по ссылке find_cd(&c, &d); // Вызов подпрограммы Фортрана find_cd printf("\n%7.2f%7.2f%7.2f%7.2f%4d", a, b, c, d, k); }
Состав файла cfun.c: void find_ab(float *a, float *b) { *a = 1.0; *b = 2.0; }
Состав файла d.f90: subroutine find_cd(c, d) !dec$attributes c :: find_cd !dec$attributes reference :: c, d real(4) :: c, d c = 3.0; d = 4.0 end subroutine find_cd
! Подпрограмма вызывается из СИ-функции ! Определяем тип соглашения о вызове ! и указываем, что параметры передаются ! по ссылке
Интерес представляет написанная на Фортране подпрограмма find_cd, в которой дополнительно к стандартным средствам Фортрана определен, вопервых, тип соглашения о вызове - С и, во-вторых, указано, что фактические параметры передаются по ссылке. Эти действия обеспечиваются атрибутами C и REFERENCE: !dec$attributes c :: find_cd !dec$attributes reference :: c, d по ссылке
! Определяем тип соглашения о вызове ! и указываем, что параметры передаются
Заметим, что тот же эффект получится и при использовании атрибутов C и REFERENCE в одном объявлении: ! Определим тип соглашения о вызове и способ передачи параметров в подпрограмму find_cd !dec$attributes c, reference:: find_cd
Необходимость использования этих атрибутов обусловлена тем, что подпрограмма find_cd вызывается из СИ-функции, причем при вызове фактическими параметрами являются адреса переменных: find_cd(&c, &d); переменных c и d
//
Фактические
параметры
-
адреса
Можно, впрочем, как и ранее, воспользоваться соглашением STDCALL. Тогда прототип процедуры find_cd в функции main опишется так: extern void __stdcall find_cd(float *c, float *d);
⎯ 174 ⎯
5. Компиляция и построение программ
Изменится и раздел объявлений подпрограммы find_cd: subroutine find_cd(c, d) ! Подпрограмма вызывается из СИ-функции !dec$attributes stdcall, reference, alias : '_find_cd@8' :: find_cd real(4) :: c, d
При любом соглашении о вызовах получение файлов cmain.obj и cfun.obj обеспечит команда CL /c cmain.c cfun.c Построим и исполняемый файл d.exe: DF d.f90 /nodformain cmain.obj cfun.obj /debug:none Опция /nodformain означает, что главная программа написана на отличном от Фортрана языке программирования.
5.9.8. Оптимизация при компиляции и построении Явно максимальный уровень оптимизации генерируемого кода устанавливается опцией /optimize:4. Этот уровень задан в команде DF по умолчанию (если, правда, отсутствует опция /debug без ключевых слов). Причем наилучшие условия для межпроцедурной оптимизации существуют, когда все файла компилируются одной командой и не задана опция /compile_only или /keep. Столь же хорошие условия для генерации быстрого приложения можно создать и при наличии команд /compile_only и /keep, указав опцией /object для всех присутствующих в команде DF файлов единый файл с объектным кодом, например: DF /compile_only /object:result.obj /optimize:4 a.f90 b.f90 c.f90 Если же в последней команде опцию /object:result.obj не задать, то будут созданы 3 объектных файла: a.obj, b.obj и c.obj - и возможности для межпроцедурной оптимизации будут существенно снижены. Меньшие уровни оптимизации, задаваемые опцией /optimize, допустимы на этапе отладки. При их задании снижаются затраты на компиляцию файлов (по сравнении с уровнем /optimize:4), но также может упасть быстродействие приложения. Выпуская рабочую версию продукта, устанавливайте максимально возможный уровень оптимизации.
5.9.9. Команда DF, параметры которой хранятся в текстовом файле Разместим в текстовом файле sam.txt параметры - опции и имена файлов - команды DF: ⎯ 175 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
/compile_only /object:result.obj /optimize:4 a.f90 b.f90 c.f90 Для запуска команды DF с этими параметрами следует набрать DF @sam.txt Если параметры не умещаются на одной строке текстового файла, то их запись продолжается на следующей строке. Символы продолжения не используются.
5.9.10. Примеры ошибочного использования команды DF Команда DF a.f90 b.f90 /link c.f90 /out:result.exe ошибочна, поскольку после опции /link можно указывать только имена тех файлов, которые передаются построителю, а файл c.f90, так же как и файлы a.f90 и b.f90, должен поступить для обработки компилятору. Ошибочна и эта команда: DF a.f90 b.f90 c.f90 /out: result.exe поскольку в ней используется опция построителя /out, которая может следовать только после опции /link. Следующая команда ошибок не содержит: DF a.f90 b.f90 c.f90 /link /out: result.exe
5.10. Ограничения компилятора и построителя Составляя программу, учитывайте приведенные в табл. 5.4 предельные значения. Таблица 5.4. Предельные значения DVF Элемент языка
Ограничение
Число параметров в одном вызове подпрограммы или функции
255
Число измерений массива
7
Протяженность по одному измерению
2,147,483,647 или возможности компьютера
Символьные константы
2000 символов
Число символов, вводимых под управлением списка ввода/вывода
2048 символов
⎯ 176 ⎯
5. Компиляция и построение программ
Число строк продолжения
99
Общая вложенность DO и IF операторов
128
Размер переменной цикла
2,147,483,647 или возможности компьютера
Вложенность групп в спецификации формата
8
Длина строки исходного текста Фортран-программы
132 символа
Вложенность INCLUDE-файлов
10
Число меток в списке вычисляемого или назначаемого оператора GOTO
500
Число знаков в операторе
3000
Число именованных common-блоков
250
Число вложенных скобок в выражении
40
Число вложений для структур
20
Длина имени
31 символ
Общий объем данных, размер массивов и программ ограничен только возможностями компьютера, а точнее - имеющимся объемом адресуемого виртуального пространства, на котором создаются и/или запускаются приложения.
5.11. Перечень опций компилятора и построителя Опции компилятора задаются либо в командой строке, и тогда они должны предшествовать опции /link (если таковая имеется), либо в диалоговом окне среды VS, открываемом при выполнении цепочки Build Settings... Команда DF может содержать только имена файлов и запускаться без опций. В этом случае действуют установленные по умолчанию значения опций. Опции построителя, если они не опущены, должны следовать после опции /link: DF a.f90 mylib.lib /link /nodefaultlib Также их можно задать в команде LINK, используемой, наряду с командой DF, для построения приложений, например: ⎯ 177 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
LINK a.obj mylib.lib /nodefaultlib Опции компилятора с разбивкой по категориям приведены в табл. 5.5. В столбце Возможные значения курсивом выделены принимаемые по умолчанию значения. В приводимой далее табл. 5.6 даны описания опций построителя. Таблица 5.5. Опции компилятора Назначение опции
Опция
Возможные значения
Опции общего назначения Уровень отладки
/[no]debug:[kyeword]
/debug:minimal | partial | full; /debug:none или /nodebug
Уровень оптимизации
/[no]optimize:level
/optimize:1 | 2 | 3 | 4; /nooptimize
Формирование файла просмотра
/[no]browser[:filename]
/browser[:filename]; /nobrowser
Задание символа для условной компиляции
/define:symbol[:integer]
/define:symbol[:integer]
Управление объемом и типом предупреждений
/[no]warn:[kyeword]
/warn:alignments | noalignments | argument_checking | noargument | declarations | nodeclarations | errors | noerrors | general | nogeneral | uncalled | nouncalled | uninitialized | nouninitialized | all | requests; /nowarn или /warn:none
Опции, обеспечивающие совместимость Совместимость с Microsoft Fortran PowerStation 4.0 (FPS4)
/[no]fpscomp:[kyeword]
/fpscomp:[no]filesfromcmd | [no]general | [no]ioformat | [no]libs | [no]logicals | [no]symbols | all; /nofpscomp или /fpscomp:none
Использовать компилятор FPS4
/oldfps
/oldfps
⎯ 178 ⎯
5. Компиляция и построение программ
Неформатные файлы других систем
/convert:kyeword и /assume:[no]byterecl
/convert:big_endian | cray | ibm | little_endian | native | vaxd | vaxg; /assume:[no]byterecl
VMS
/[no]vms
/vms; /novms
Выбрать подключаемую библиотеку Фортрана 77
/[no]f77rtl
/f77rtl; /nof77rtl
Опции, используемые при отладке Уровень отладки
[no]debug
/debug:minimal | partial | full; /debug:none или /nodebug
Файл с отладочной информацией
/[no]pdbfile[:filename]
/pdbfile[:filename]; /nopdbfile
Компиляция файлов с буквой D в первой позиции строки
/[no]d_lines
/d_lines; /nod_lines
Внешние процедуры и пересылка параметров Установленные по /[no]iface:kyeword умолчанию соглашения о вызовах
/iface:cref | default | stdref | [no]mixed_str_len_arg; /iface:(default, mixed_str_len_arg)
Длина строки параметра
/[no]iface:mixed_str_len_arg
/[no]iface:mixed_str_len_arg
Интерпретация имен
/names:kyeword
/names:as_is | lowercase | uppercase
Добавлять подчеркивание перед внешними именами
/assume:[no]underscore
/assume:underscore | nounderscore
Опции операций с плавающей точкой Обработка арифметических исключений
/fpe:level
/fpe:0 | 1 | 2 | 3 | 4
Последовательность операций с плавающей точкой
/[no]fltconsistency
/fltconsistency; /nofltconsistency
Разновидность типа констант /[no]fpconstant с плавающей точкой
⎯ 179 ⎯
/fpconstant; /nofpconstant
О. В. Бартеньев. Visual Fortran: новые возможности
Работа с данными Выравнивание данных в общем блоке
/alignment:kyeword
/align:commons | nocommons | dcommons
Выравнивание данных производного типа
/alignment:[no]records
/align:records | norecords;
Разновидность типа целых и логических данных
/integer_size:size
/integer_size 16 | 32
Разновидность типа целых констант
/[no]intconstant
/intconstant | nointconstant
Разновидность типа вещественных и комплексных констант
/real_size:size
/real_size:32 | 64
Автоматические или статические данные
/[no]automatic или /[no]static
/automatic | noautomatic или /static | /nostatic
Единицы измерения спецификатора RECL неформатных файлов
/assume:[no]byterecl
/assume:byterecl | nobyterecl
Совместное использование /assume: одной области памяти [no]dummy_aliases формальными параметрами процедур
/assume:dummy_aliases | nodummy_aliases
Опции языка программирования Проверка следованию правилам стандарта
/std[:strict]
/std | /std:strict
Использование семантики Фортрана 66
/[no]f66
/f66 | nof66
Использование альтернативного синтаксиса оператора PARAMETER
/[no]altparam
/altparam | noaltparam
Задание свободного или фиксированного формата исходного текста
/[no]free или /[no]fixed
/free / nofree или /fixed / nofixed
⎯ 180 ⎯
5. Компиляция и построение программ
Длина строки в исходном файле с фиксированным форматом
/[no]extend_source[:size]
/extend_source; /extend_source:72 | 80 | 132; /noextend_source
Интерпретация имен
/names:kyeword
/names:as_is | lowercase | uppercase
Опции библиотек и объектных файлов Компиляция без построения
/compile_only или /c
/compile_only или /c
Главная программа написана не на Фортране
/nodfmain
/nodfmain
Рекурсивные процедуры
/[no]recursive
/recursive | norecursive
Включение имен библиотек /[no]libdir в объектный файл
/libdir | nolibdir
Типы библиотек, используемые для поиска ненайденных ссылок (вызовов процедур)
/libs:keyword
/libs:dll | dllmulti | multi | static | qwin | qwins
Начало опций построителя
/[no]link
/link | nolink
Создание динамической библиотеки
/dll
/dll
Создание приложения Windows
/winapp
/winapp
Создание карты построителя
/[no]map[:file]
/map[:file]; nomap
Опции листинга Создание листинга на Ассемблере
/[no]asmfile[:file] или /[no]asmattributes:keyword
/asmfile[:file] | noasmfile; /asmattributes:source | machine | all | none; /noasmattributes
Листинг исходного файла
/[no]list[:file]
/list[:file]; /nolist
Содержание листинга исходного файла
/show:keyword или /[no]machine_code
/show:code | include | map | nomap; /machine_code | /nomachine_code
⎯ 181 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Разные опции компилятора Предельное значение на число ошибок компиляции
/[no]error_limit[:count]
/error_limit[:count]; /noerror_limit; /error_limit:30;
Директория для размещения модулей
/module[:path]
/module[:path]
Путь для поиска модулей и /[no]include[:path] и /assume:[no]source_include INCLUDE-файлов
/include[:path] | noinclude; /assume:source_include | nosource_include
Имя исполняемого файла или библиотеки DLL
/exe[:filename]
/exe[:filename]
Имя объектного файла
/[no]object[:filename]
/object[:filename]; /noobject
Генерация файла просмотра
/[no]browser[:filename]
/browser[:filename]; /nobrowser
Выполнять только проверку синтаксиса
/[no]syntax_only
/syntax_only; /nosyntax_only
Отбивка строк исходного файла завершающими пробелами
/[no]pad_source
/pad_source; /nopad_source
Показать справочные данные
/help или /?
/help или /?
Вставить строку string в объектный файл
/V"string"
/V"string"
Задание пользовательского /[no]extfor:ext расширения файлов для компилятора
/extfor:ext; /noextfor
Задание пользовательского /[no]extfpp:ext расширения файлов для предпроцессора
/extfpp:ext; /noextfpp
Задание пользовательского /[no]extlink:ext расширения файлов для построителя
/extlink:ext; /noextlink
⎯ 182 ⎯
5. Компиляция и построение программ
Показывать или не отображать данные об авторских правах и о версии компилятора
/nologo; /what
Показывать информацию о /verbose ходе выполнения компиляции
/nologo; /what
/verbose
Оптимизация и генерация кода Уровень оптимизации
/[no]optimize:level
/optimize:1 | 2 | 3 | 4; /nooptimize
Встраивание процедур
/[no]inline:kyeword
/inline:none | manual | size | speed | all; /noinline
Раскрутка циклов
/unroll:count
/unroll:count (0 ≤ count ≤ 16)
Изменение порядка выполнения операций с плавающей точкой
/assume:[no]accuracy_sensitive
/assume:noaccuracy_sensiti ve | noaccuracy_sensitive
Проверка параметров встроенных процедур
/math_library:fast
/math_library:fast
Проверка вызовов процедур математической библиотеки
/math_library:check
/math_library:check
Опции предпроцессора Задание символа для условной компиляции
/define:symbol[:integer]
/define:symbol[:integer]
Проверки в процессе выполнения приложения Проверка границ массивов и строк
/check:[no]bounds
/check:bounds | nobounds
Проверка согласованности формата и данных
/check:[no]format
/check:format | noformat
Проверка согласованности длины формата
/check:[no]output_conversion
/check:output_conversion | nooutput_conversion
Проверка переполнения в операциях с целыми числами
/check:[no]overflow
/check:overflow | nooverflow
⎯ 183 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Проверка степенных выражений
/check:[no]power
/check:power | nopower
Проверка потери значимости в операциях с числами с плавающей точкой
/check:[no]underflow
/check:underflow | nounderflow
Замечание. Процессом компиляции можно также управлять и из программы, включая в нее директивы - специальные инструкции, которые сообщают компилятору, какие надо предпринять действия при компиляции программы. Перечень директив DVF приведен в прил. 1. Таблица 5.6. Опции построителя Опция
Назначение
/align:number
Задает выравнивание каждой секции в пределах линейного адресного пространства программы
/base:address | @filename,key
Задает базовый адрес программы
/comment:["]comment["]
Вставляет строку-комментарий в заголовок исполняемого файла или DLL
/debug
Создает отладочную информацию для исполняемого файла или DLL
/debugtype:cv | coff | both
Задает формат генерации отладочной информации
/def:filename
Передает файл определений построителю
/defaultlib:libraries
Добавляет одну или более библиотек в список библиотек, в которых построитель ищет внешние ссылки
/dll
Создает в качестве выходного файла библиотеку DLL
/entry:function
Устанавливает стартовый адрес исполняемого файла или DLL
/export
Экспортирует во внешний файл функцию из программы; опция имеет синтаксис: /export:entryname[=internalname][,@ordinal [,noname]] [,data]
/fixed
Сообщает операционной системе, что программа должна загружаться по своему основному адресу
⎯ 184 ⎯
5. Компиляция и построение программ
/force:[multiple | unresolved]
Сообщает построителю о необходимости создания исполняемого файла или DLL, даже если символ, на который выполняется ссылка, не определен или определен многократно
/heap:reserve,[commit]
Размер произвольно распределяемой памяти в байтах
/implib:filename
Задает имя импортируемой библиотеки, которую создает построитель, когда программа имеет экспортируемые процедуры или данные
/include:symbol
Сообщает построителю, что нужно добавить символ в таблицу символов
/incremental:yes | no
Определяет, как выполняется нарастающая генерация файла
/map[:filename]
Передает построителю директиву о генерации файла с картой построения
/nodefaultlib[:library]
Сообщает построителю о необходимости удалить заданные для поиска внешних ссылок библиотеки из списка таких библиотек
/noentry
Не дает построителю выполнять ссылку на _main в DLL
/nologo
Предотвращает отображение информации об авторских правах и версии построителя
/opt:{ref | noref}
Управляет уровнем оптимизации, которую осуществляет построитель
/order:@filename
Управляет порядком размещения данных в создаваемом файле
/out:filename
Меняет задаваемое по умолчанию имя генерируемого построителем файла
/pdb[:filename]
Задает имя файла (взамен имени, известного по умолчанию), содержащего отладочную информацию
/profile
Создает файл, который может быть использован программой profiler, анализирующей созданное приложение
/release
Вставляет контрольную сумму в заголовок исполняемого файла
/stack:reserve[, commit]
Задает размер стека в байтах
⎯ 185 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
/stub:filename
Подсоединяет DOS-программу к WIN32-программе
/subsystem
Сообщает операционной системе, как исполнять EXE-файл. Имеет синтаксис: /subsystem:{console | windows | native}[,major [.minor]]
/verbose[:lib]
Посылает информацию о ходе построения в выходное окно
/version:major[.minor]
Сообщает построителю о необходимости разместить информацию о версии программы в заголовке создаваемого им EXE или DLL файла
/warn:level
Задает объем выводимых построителем предупреждений (0 ≤ level ≤ 2)
5.12. Распределение опций построителя по категориям VS В среде VS доступ к опциям компилятора и построителя становится возможным после выполнения цепочки Build - Settings... Опции компилятора и построителя распределены по категориям. В табл. 5.7. перечислены все опции построителя, однако для опций, которые нельзя задать ни в одной из LINK-категорий, в столбце Категория указано: “Командная строка”. Для прочих опций в этом столбце приводится категория, выбрав которую можно задать или изменить значение опции. Опции, не попадающие в LINK-категории, могут быть заданы в закладке Custom Build, доступ к которой выполняется по той же цепочке: Build Settings... Таблица 5.7. Категории VS для опций построителя Опция
Категория VS
/align:number
Командная строка
/base:address | @filename,key
Output
/comment:["]comment["]
Командная строка
/debug
Debug
/debugtype:cv | coff | both /def:filename
"
Название опции в VS
– Base Address – General Debug Info Microsoft Format | COFF Format | Both Formats
Командная строка
⎯ 186 ⎯
–
5. Компиляция и построение программ
/defaultlib:libraries
"
“
–
/dll
"
“
–
/entry:function
Output
/export
Командная строка
/fixed
"
Entry-Point Symbol “
/force:[multiple | unresolved]
Customize
/heap:reserve,[commit]
Командная строка
/implib:filename
"
– – Force File Output
“
– –
/include:symbol
Input
Force Symbol Reference
/incremental:yes | no
Customize
Link Incrementally
/map[:filename]
Debug
Mapfile Name
/map /nodefaultlib[:library] /nodefaultlib
"
Generate Mapfile
Input
Ignore Libraries
"
Ignore All Default Libraries
/noentry
Командная строка
/nologo
Customize
/opt:{ref | noref}
Командная строка
/order:@filename /out:filename /pdb[:filename]
"
– Suppress Start up Banner
“
Customize "
– – Output File Name Program Database Name
/profile
General
Enable Profiling
/release
Командная строка
/stack:reserve[, commit]
Output
Stack Allocations
/stub:filename
Input
MS-DOS Stub File Name
/subsystem
Командная строка
–
–
/verbose[:lib]
Customize
Print Progress Messages
/version:major[.minor]
Output
Version Information
/warn:level
Командная строка
⎯ 187 ⎯
–
О. В. Бартеньев. Visual Fortran: новые возможности
5.13. Использование опций FPS в команде DF Пользователи FPS, привыкшие к синтаксису Фортрана Microsoft, могут продолжать применять опции FPS вместо опций или вместе с опциями DVF в команде DF. Соответствие между опциями FPS и DVF приведено в табл. 5.8. Отметим, что опции FPS чувствительны к регистру и, следовательно, должны записываться в команде в полном соответствии с табл. 5.8. Таблица 5.8. Соответствие между опциями командной строки FPS и DVF Опции FPS
Назначение
Опции DVF
Управление листингом /FA
Листинг на Ассемблере
/asmfile[:file]
/FAc
Листинг на Ассемблере c машинным кодом
/asmattributes:machine и /asmfile[:file]
/FAs
Листинг на Ассемблере c исходным кодом
/asmattributes:source и /asmfile[:file]
/FAcs
Листинг на Ассемблере c машинным и исходным кодом
/asmattributes:all и /asmfile[:file]
/Fa[file]
Вывод листинга на Ассемблере в файл
/noasmattributes и /asmfile[:file]
/Fc[file]
Вывод листинга на Ассемблере c машинным и исходным кодом в файл
/asmattributes и /asmfile[:file]
/Fl[file]
Вывод листинга на Ассемблере c машинным кодом в файл
/asmattributes(nosource, machine) и /asmfile[:file]
/Fs[file]
Листинг исходных файлов с откомпилированным кодом
/list[:file] и /show:map
Генерация кода /FR[file]
Генерация файла просмотра
/browser[:file]
/Ob2
Автоматическое встраивание кода; используется совместно с /Ox
/inline:speed
/Od
Отказ от оптимизации кода
/optimize:0 и /math_library:check
⎯ 188 ⎯
5. Компиляция и построение программ
/Op
Улучшение последовательности выполнения операций с плавающей точкой
/fltconsistency
/Ox
Полная оптимизация без проверки ошибок
/optimize:4 и /math_library:nocheck
/Oxp
Оптимизация скорости, встраивание процедур, проверка ошибок
/optimize:4 и /fltconsistency
/Zp[n]
Упаковка структур по n-байтовой границе (n - это 1, 2 или 3)
/alignment[:keyword]
Расширения языка /4Lnn
Длина строки в файле с исходным кодом (nn - это 72, 80 или 132)
/extended_source[:nn]
/4Yb или /4Nb
Задать или отключить расширенную проверку ошибок
/check[:keyword]
/4Yb или /4Nb
Предупреждения о необъявленных переменных
/warn:[no]declarations
/W0
Отказ от предупреждений
/nowarn
/W1
Выводить предупреждения
/warn:general
/WX
Интерпретировать все предупреждения как ошибки
/warn:(general,errors)
Стандарты языка, исходный код и данные /4Ya или /4Na
Все переменные будут автоматическими или статическими
/[no]automatic или /[no]static
/4Yaltparam /4Naltparam
Использование альтернативного синтаксиса оператора PARAMETER
/[no]altparam
/4Yf или /4Nf
Задание свободного формата исходного кода
/[no]free; /[no]fixed
/4fps1
В DVF игнорируется
/4I2
Установить задаваемую по умолчанию разновидность типа целых и логических величин равной двум (KIND = 2)
⎯ 189 ⎯
– /integer_size::16
О. В. Бартеньев. Visual Fortran: новые возможности
/4R8
Установить задаваемую по умолчанию разновидность типа вещественных величин равной восьми (KIND = 8)
/real_size:64
/4Ys или /4Ns
Строгое следование последнему стандарту Фортрана
/std:strict
Директивы условной компиляции /Dsymbol[=int]
Задание символа, используемого при условной компиляции
/define:symbol[=int]
/4ccd
Обрабатывать строки с буквой d или D в первой позиции как комментарий
/d_lines
/4Yportlib или /4Nportlib
Автоматическое подсоединение к обеспечивающей переносимость программ библиотеке
/4Yportlib или /4Nportlib
/Fd[file]
Создание или отказ от создания PDB-файла
/[no]pdb[:filename]
/Fe[file]
Задание имени EXE или DLL файла
/exe[:filename]
/Fm[file]
Создание файла с картой работы построителя
/map[:filename]
/Fo[file]
Задание имени объектного файла
/object[:filename]
/Gna
Оставлять внешние имена без изменений и обрабатывать имена, содержащиеся в исходном коде, как чувствительные к регистру
/names:as_is
/GNl
Записать внешние имена строчными буквами и игнорировать регистр в именах, содержащихся в исходном коде
/names:lowercase
/GNu
Записать внешние имена прописными буквами и игнорировать регистр в именах, содержащихся в исходном коде
/names:uppercase
/Ipath
Задает путь поиска модулей и включаемых файлов
/[no]include[:path]
⎯ 190 ⎯
5. Компиляция и построение программ
/LD
Создание динамической библиотеки
/dll
/MD
Построитель использует многониточные динамические библиотеки
/libs:dllmulti
/MDs
Построитель использует однониточные динамические библиотеки
/libs:dll
/MG
Построитель использует библиотеки для Windows-приложений
/winapp
/ML
Построитель использует однониточные статические библиотеки
/libs:static
/MT
Построитель использует многониточные статические библиотеки
/libs:multi
/MW
Создание приложения QuickWin
/libs:qwin
/MWs
Создание приложения со стандартной графикой
/libs:qwins
/Tffile
Указывает, что файлы являются файлами Фортрана с исходным текстом
/source:filename
/V"string"
Помещает строку string в объектный файл
/V"string"
/Z7
Задает режим выдачи всей отладочной информации и размещает ее в объектном файле
/dubug:full и /nopdbfile
/Zd
Задает режим выдачи минимальной отладочной информации
/dubug:minimal и /pdbfile
/Zi
Задает режим выдачи всей отладочной информации и размещает ее в PDB-файле
/dubug:full и /pdbfile
/Zl
Отменяет запись имен библиотек в объектный файл
/nolibdir
/Zs
Выполняется только проверка синтаксиса
/syntax_only
⎯ 191 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
/link [option]
Все следующие за этой опцией параметры передаются построителю
/link [option]
Специальные опции /?, /help
Выдает справочную информацию об опциях компилятора и построителя
/?, /help
/nologo
Не отображаются данные об авторских правах
/nologo
⎯ 192 ⎯
6. Повышение быстродействия программ 6.1. Введение Быстродействие программы определяется следующими факторами: •
разработанным алгоритмом и выбранными структурами данных;
•
качеством программирования (кодирования) алгоритма;
•
способом и параметрами компиляции (compile) исходного кода и построения (link) исполняемого файла. Ниже будут рассмотрены приемы, позволяющие оптимизировать быстродействие программы по второму и третьему факторам. Анализируя программу на предмет оценки ее быстродействия, следует прежде выявить фрагменты, в которых могут происходить существенные потери времени. К ним относятся прежде всего циклы, анализу которых далее уделяется наибольшее внимание. Впрочем, не следует пренебрегать и иными фрагментами программы. Логичным будет такой подход к записи исходного кода: программист вырабатывает стиль программирования, учитывающий все факторы, влияющие на быстродействие программы, и придерживается этого стиля в процессе работы над каждым ее фрагментом. Разумеется, стиль программирования должен постоянно совершенствоваться.
6.2. Время выполнения программы Время выполнения программы или ее фрагмента определяется приводимой ниже функцией timer, которая использует встроенную подпрограмму DATE_AND_TIME. Задача. Сравнить время выполнения вычислений суммы и произведения элементов векторов, имеющих один размер. Тип векторов REAL(4) и REAL(8). Выполнить вычисления, первоначально используя циклы, а затем встроенные функции SUM и PRODUCT. program find_time integer(4), parameter :: n = 100000 ! Размер массивов array4 и array8 integer(4), parameter :: m = 100 ! Число вычислений сумм и произведений integer(4) :: i, j real(4), dimension(n) :: array4 = 1.000001 real(8), dimension(n) :: array8 = 1.00000123456789_8
⎯ 193 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
real(4) :: s4, p4 real(8) :: s8, p8 real(8) :: start_time4, finish_time4 ! Время начала и завершения вычислений real(8) :: start_time8, finish_time8 ! соответственно для типов REAL(4) и REAL(8) real(8) :: timer ! Функция, возвращающая процессорное время ! в миллисекундах, имеет тип REAL(8) start_time4 = timer( ) ! Начало вычислений s4 и p4 для типа REAL(4) do j = 1, m ! Сумму и произведение вычисляем m раз s4 = 0.0; p4 = 1.0 do i = 1, n ! Используем цикл s4 = s4 + array4(i) p4 = p4 * array4(i) end do end do finish_time4 = timer( ) ! Конец вычислений s4 и p4 для типа REAL(4) start_time8 = timer( ) ! Начало вычислений s8 и p8 для типа REAL(8) do j = 1, m ! Сумму и произведение вычисляем m раз s8 = 0.0_8; p8 = 1.0_8 do i = 1, n s8 = s8 + array8(i) p8 = p8 * array8(i) end do end do finish_time8 = timer( ) ! Конец вычислений s8 и p8 для типа REAL(8) print 123, '(4)', finish_time4 - start_time4 print 123, '(8)', finish_time8 - start_time8 print *, 'Run time with functions SUM and PRODUCT' start_time4 = timer( ) ! Начало вычислений s4 и p4 для типа REAL(4) do j = 1, m ! Сумму и произведение вычисляем m раз s4 = sum(array4) ! Используем встроенную функцию SUM p4 = product(array4) ! Используем встроенную функцию PRODUCT end do finish_time4 = timer( ) ! Конец вычислений s4 и p4 для типа REAL(4)
⎯ 194 ⎯
6. Повышение быстродействия программ
start_time8 = timer( ) ! Начало вычислений s8 и p8 для типа REAL(8) do j = 1, m ! Сумму и произведение вычисляем m раз s8 = sum(array8) p8 = product(array8) end do finish_time8 = timer( ) ! Конец вычислений s8 и p8 для типа REAL(8) print 123, '(4)', finish_time4 - start_time4 print 123, '(8)', finish_time8 - start_time8 123 format(1x, 'Run time for REAL', a3, f10.3) end program find_time function timer( ) ! Определение процессорного времени timer real(8) :: timer ! в миллисекундах integer(4) :: ival(8) call date_and_time(values = ival) timer = dble(ival(8))*0.001_8+dble(ival(7))+dble(ival(6))*60.0_8+dble(ival(5))*3600.0_8 end function timer
Замечание. В Фортране 95 процессорное время возвращает встроенная подпрограмма CPU_TIME (см. прил. 4), которой также можно воспользоваться для оценки времени вычислений.
6.3. Выравнивание данных 6.3.1. Размещение данных в памяти Размещение данных в оперативной памяти влияет на время доступа к ним и, следовательно, на быстродействие программы. С позиции быстродействия размещение подразделяется на выравненное и невыравненное. При выравненном размещении начальный адрес каждого скаляра кратен параметру разновидности его типа. (Напомним, что единицей измерения адреса памяти является байт.) Например, скаляр типа REAL(8) выравнен по своей естественной границе, если его адрес кратен восьми, в противном случае размещение скаляра будет невыравненным. Массив будет выравненным, если выравнены все его элементы. Понятно, что всегда выравнены данные стандартного символьного типа, имеющие параметр разновидности KIND, равный единице. Для увеличения быстродействия размещение данных должно быть выравненным. ⎯ 195 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
6.3.2. Невыравненные данные Digital Visual Fortran (DVF) по возможности выравнивает данные. Однако в операторах EQUIVALENCE, COMMON, TYPE - END TYPE и STRUCTURE - END STRUCTURE могут появляться невыравненные данные. Рассмотрим common-блок a1: logical(2) :: flag integer(4), dimension(3) :: iarray character(len = 7) string1 common/a1/ flag, iarray, string1
Расположение данных этого блока отобразим на рис. 6.1. flag 0
iarray(1) 2
iarray(2) 6
iarray(3) 10
s
t
r
i
n g 1
14
19
Рис. 6.1. Размещение элементов common-блока а1
Из рис. 6.1 видно, что адрес элементов массива iarray не кратен четырем, т. е. массив невыравнен. Чтобы избавиться от этого недостатка, нужно выполнить компиляцию программы с опцией /align:commons или /align:dcommons. В случае /align:commons выравнивание данных выполняется по 4-байтовой границе и компилятор добавит после байтов, занятых переменной flag, 2 пустых байта. Результат отображен на рис. 6.2. Компиляция с опцией /align:dcommons обеспечит выравнивание данных по 8-байтовой границе. flag 2
iarray(1) iarray(2) iarray(3) 4
6
10
s
t
r
i
n
g
14
1 21
Рис. 6.2. Выравненные данные common-блока а1
Однако правильнее добиться эффекта расположение данных в общем блоке:
выравнивания,
изменив
common/a2/ iarray, flag, string1
В этом случае прибегать при компиляции к опции /align:commons не придется: данные будут выравнены естественным образом (рис. 6.3). iarray(1)
iarray(2) 4
iarray(3) 8
12
flag
s
t
r
i
n
g
14
Рис. 6.3. Естественное выравнивание элементов common-блока а2
⎯ 196 ⎯
1 19
6. Повышение быстродействия программ
Скаляры производного типа DVF выравнивает по 8-байтовой границе. Рассмотрим объявления: type thing integer(4) :: thing_id real(4) :: price character(10) :: descr end type thing type(thing) :: item = thing(55, 27.98, 'furcoat')
Каждый элемент массива item типа thing займет приведенный на рис. 6.4 отрезок памяти, в котором последние 6 байт будут добавлены DVF для выравнивания данных. Thing_id
price 4
f
u r
c o a
t
8
18
24
Рис. 6.4. Отрезок памяти под скаляр производного типа item
Однако если при объявлении производного типа указан оператор SEQUENCE, то DVF не сможет добавить байты, необходимые для выравнивания данных, что приведет к снижению быстродействия. Следовательно, в этом случае программист сам при формировании производного типа должен позаботиться о выравнивании данных.
6.3.3. Сообщения о невыравненных данных Невыравненные данные обнаруживаются компилятором, который, если не задана опция /warn:noalignments, предупреждает об обнаруженных дефектах. Так, при компиляции программы, содержащей приведенный выше common-блок /a1/, компилятор выдаст размещенное на двух строчках сообщение: Warning: Alignment of variable or array is inconsistent with its type [IARRAY] integer(4), dimension(3) :: iarray
6.3.4. Как выравнивать данные Придерживайтесь следующих рекомендаций: •
не используйте оператор EQUIVALENCE, заменяя его автоматическими или размещаемыми массивами или ссылками, когда стоит задача экономии памяти, ссылками для создания псевдонимов и встроенной функцией TRANSFER для отображения одного типа данных в другой; ⎯ 197 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
•
•
•
•
минимизируйте использование common-блоков, заменяя обеспечиваемое ими ассоциирование памяти на use-ассоциирование, т. е. размещайте предназначенные для общего использования данные в модулях; не передавайте объекты производного типа в процедуры через параметры, а размещайте их в модулях и заменяйте ассоциирование параметров use-ассоциированием. Это позволит избежать использования оператора SEQUENCE, который нужно применять при объявлении производных типов, если объекты этих типов передаются в процедуры через их параметры; не используйте для объявления производных типов операторы STRUCTURE - END STRUCTURE, которые являются расширением DVF над стандартами Фортрана, заменяя их на операторы TYPE - END TYPE; при работе с common-блоками (если вы не отказались от их использования), с производными типами данных, чтобы добиться выравнивания данных, в первую очередь размещайте объекты с наибольшей разновидностью типа, например:
complex(8) :: z real(8) :: aval, array(20) integer(4) :: iarray(30) character(15) :: string common/a3/ z, aval, array, iarray, string
! Параметр разновидности типа KIND = 8 ! Параметр разновидности типа KIND = 4 ! Параметр разновидности типа KIND = 1
Следствие. При использовании в операторах COMMON, TYPE - END TYPE и STRUCTURE - END STRUCTURE смеси числовых и символьных данных прежде размещаются числовые данные.
6.3.5. Опции компилятора, управляющие выравниванием В common-блоках и производных типах данных выравнивание управляется приведенными в табл. 6.1 опциями. DVF выравнивает данные, добавляя при необходимости после размещения очередного элемента пустые байты подобно тому, как это представлено на рис. 6.1.
⎯ 198 ⎯
6. Повышение быстродействия программ
Таблица 6.1. Опции компилятора, отвечающие за выравнивание данных Опция
Действие
/align:commons
Выравнивает данные в common-блоках по 4-байтовой границе, если на задана опция /fast. По умолчанию используется опция /align:nocommons
/align:dcommons
Выравнивает данные в common-блоках по 8-байтовой границе, если на задана опция /fast. По умолчанию используется опция /align:nocommons
/align:norecords
Отменяет естественное выравнивание данных производных типов, размещая их по 1-байтовой границе. По умолчанию используется опция /align:records, если опущена опция /vms. Если задана опция /vms и в явном виде не указана опция /align:records, то применяется /align:norecords
/align:records
Обеспечивает естественное выравнивание данных. Необходимость в ее применении может возникнуть, если применяется опция /vms
/vms
Обеспечивает генерацию кода, пригодного для использования в OpenVMS VAX-системе, ранее называемой VAX FORTRAN. В части выравнивания данных устанавливает опцию /align:norecords, т. е. для выравнивания потребуется применения опции /align:records. По умолчанию действует опция /novms
6.4. Оптимизация исходного кода 6.4.1. Эффективное использование массивов При работе с массивами следует по возможности избегать циклов. Рассмотрим массив array: real(4), dimension(100, 100) :: array = 0.0
Придерживайтесь таких рекомендаций: 1) используйте, заменяя циклы, присваивание массивов: array = array + 1.0 array(1:50, :) = -4.0
! И так далее
2) применяйте вывод всего массива, и желательно в двоичный или неформатный файл: open(10, file = 'a.dat', form = 'binary')
⎯ 199 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
write(10) a
! Вывод всего массива
Замечание. Аналогичным образом работайте с массивом-компонентом производного типа, например: type points integer(4) :: npoints real(4), dimension(2, 100) :: coords end type points type(points) :: curve = points(85, 20.0) curve%coords = curve%coords * 0.5 write(10) curve%coords
3) если двумерный массив используется во вложенном цикле, во внешнем цикле меняйте индекс столбца, а во внутреннем - индекс строки. Это обеспечит естественный и, следовательно, более быстрый доступ к элементам массива, поскольку в Фортране они размещаются в памяти ЭВМ по столбцам, а не по строкам, как это предусмотрено, например, в СИ. Замерим время вычислений при правильной и неоптимальной организации вложенных циклов. program toappr integer(4), parameter :: n = 1000 real(4), dimension(n, n) :: array1, array2 real(8) :: start_time1, finish_time1 ! Время начала и завершения вычислений real(8) :: start_time2, finish_time2 ! соответственно при правильной и неоптимальной ! организации вложенных циклов real(8) :: timer ! Функция, возвращающая процессорное время ! в миллисекундах, имеет тип REAL(8) array1 = 1.1; array2 = 1.5 ! Инициализация массивов ! Правильная организация вложенного цикла обеспечивает естественный доступ ! к элементам массива по столбцам start_time1 = timer( ) ! Начало вычислений do j = 1, n ! Правильно: сначала задается столбец, do i = 1, n ! а затем меняется индекс строки array1(i, j) = array1(i, j) + array2(i, j) * 3.3 end do end do finish_time1 = timer( ) ! Конец вычислений array1 = 1.1; array2 = 1.5 ! Повторная инициализация массивов ! Неоптимальная организация вложенного цикла - неестественный доступ
⎯ 200 ⎯
6. Повышение быстродействия программ
! к элементам массива по строкам start_time2 = timer( ) ! Начало вычислений do i = 1, n ! Так программировать не надо. Обеспечьте do j = 1, n ! доступ к элементам массива по столбцам array1(i, j) = array1(i, j) + array2(i, j) * 3.3 end do end do finish_time2 = timer( ) ! Конец вычислений print *, (finish_time1 - start_time1) / (finish_time2 - start_time2) ! Результат: 0.352941176460518 end program toappr function timer( ) ! Определение процессорного времени timer real(8) :: timer ! в миллисекундах integer(4) :: ival(8) call date_and_time(values = ival) timer = dble(ival(8))*0.001_8+dble(ival(7))+dble(ival(6))*60.0_8+dble(ival(5))*3600.0_8 end function timer
Замечание. При построении используемого для замеров исполняемого файла была установлена конфигурация Release, в которой полностью отсутствует отладочный код, т. е. задана опция /debug:none. Аналогично следует организовывать вложенные циклы с массивами, число измерений которых более двух: во внутреннем цикле должен изменяться самый левый индекс, а во внешнем - самый правый; 4) используйте вместо циклов выражения с массивами. Так, в последнем примере циклы заменяются выражением array1 = array1 + array2 * 3.3
5) используйте вместо циклов встроенные функции для массивов (см. задачу в разд. 6.2); 6) используйте как встроенные, так и созданные вами элементные функции, например: array = abs(array) |x|
! ABS - элементная функция; abs(x) вернет
7) заменяйте DO-циклы с массивами операторами и конструкциями WHERE и FORALL, например цикл do j = 1, n do i = 1, n if(array1(i, j) > 0) array1(i, j) = array1(i, j) + array2(i, j) * 3.3 end do
⎯ 201 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
end do
заменяется на оператор where(array1 > 0.0) array1 = array1 + array2 * 3.3
Цикл do j = 1, n do i = 1, n array1(i, j) = array1(i, j) + (i + j) * array2(i, j) end do end do
заменяется оператором forall(i = 1:n, j = 1:n) array1(i, j) = array1(i, j) + (i + j) * array2(i, j)
8) с целью повышения эффективности использования быстрой памяти (кеш-памяти) не делайте границу многомерного массива равной целой степени числа 2, например равной 1024. Так, массив real(4), dimension(1024, 50) :: array
замените на массив real(4), dimension(1030, 50) :: array
Внося некоторую избыточность, вы повышаете быстродействие.
6.4.2. Организация быстрого ввода/вывода Затраты времени на В/В снизятся, если придерживаться следующих правил: 1) по возможности используйте неформатные файлы вместо форматных. Так, вывод в файл, подсоединенный к устройству 10, выполнится значительно быстрее вывода в файл, который подсоединен к устройству 20: real(4), dimension(100, 20) :: array open(10, file = 'a.dat', form = 'unformatted') ! или form = 'binary' open(20, file = 'a.txt', form = 'formatted') write(10) array ! Доступ к файлу a.dat происходит быстрее, write(20, '(20f8.3)') array ! чем к файлу a.txt
2) выполняйте В/В всего массива или всей строки не используя циклов; 3) если все же при передаче многомерных массивов необходимо организовать вложенные циклы, то изменяйте индексы по рассмотренному выше правилу: во внутреннем цикле должен изменяться самый левый индекс, а во внешнем - самый правый. Это ⎯ 202 ⎯
6. Повышение быстродействия программ
обеспечит доступ к элементам массива в порядке их размещения в оперативной памяти, что, естественно, ускорит передачу данных; 4) используйте, если позволяют ресурсы, для хранения промежуточных результатов оперативную память, а не внешние файлы; 5) применяйте в случае форматного В/В при программировании формата целочисленное выражение вместо символьной строки, поскольку в первом случае формат определяется единожды - при компиляции, а во втором - спецификация формата, строка form, формируется в процессе исполнения программы: real(4), dimension(1000) :: array integer(4) :: i character(15) :: form ... ! Вычисляется значение переменной n ! Этот способ задания формата лучше, чем формирование строки form, ! содержащей спецификацию формата ! - выражение в дескрипторе преобразований print '(1x, f8.3)', (array(i), i = 1, n) write(form, '(a, i5, a)') '(1x', n, 'f8.3)' ! Формируем строку формата form print form, (array(i), i = 1, n) ! Вывод по формату form
6) создавайте условия для декомпозиции используемых в операторах В/В циклических списков. Для этого переменная цикла должна быть целочисленной, не должна быть формальным параметром, принадлежать оператору EQUIVALENCE и обладать атрибутом VOLATILE, а спецификация формата в случае форматной передачи данных не должна иметь целочисленных выражений в дескрипторе преобразований. Пример циклического списка, имеющего и иное название - встроенный DO-цикл с параметром: write(10, '(20f8.3)') (array(i), i = 1, n)
! Циклический список из n элементов
Пояснение. Обычно каждый элемент списка В/В обращается к процедурам В/В библиотеки DVF. Временные затраты на эти обращения значительны. С целью их уменьшения встроенный DO-цикл замещается компилятором на несколько (до семи) вложенных DO-циклов, использующих для вывода оптимизированную процедуру В/В DVF, которая может осуществлять передачу порциями, содержащими несколько элементов В/В; 7) для увеличения объема передаваемых данных при одном обращении к диску попытайтесь увеличить значение спецификатора BUFFERCOUNT оператора OPEN, но не меняйте значение спецификатора BLOCKSIZE ⎯ 203 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
того же оператора, поскольку оно подбирается оптимальным для используемого устройства. Влияние BUFFERCOUNT на скорость передачи данных должно быть установлено экспериментально; 8) не задавайте значение спецификатора RECL оператора OPEN большим, чем размер буфера В/В (этот размер определяется спецификатором BLOCKSIZE), так как передача избыточных данных, незначительно заполняющих буфер, малопроизводительна; 9) значение спецификатора RECL выбирайте таким образом, чтобы буфер В/В заполнялся наилучшим образом. Буфер будет заполнен полностью, если его размер кратен RECL или, наоборот значение RECL кратно размеру буфера, например: размер буфера равен 8192, а RECL = 1024 или RECL = 16384; 10) используйте оптимальный с позиции быстродействия тип записей, задаваемый в операторе OPEN спецификатором RECORDTYPE: • для последовательных файлов наибольшую производительность обеспечат файлы с фиксированной длиной записи (RECORDTYPE = 'FIXED'); • в случае последовательных неформатных файлов используйте записи переменной длины (RECORDTYPE = 'VARIABLE'); • в последовательных файлах с записями переменной длины задавайте RECORDTYPE = 'STREAM_LF'.
6.4.3. Дополнительные приемы оптимизации кода Они таковы: •
используйте наиболее эффективные с позиции быстродействия типы данных. Перечислим типы данных в порядке снижения их эффективности: INTEGER, LOGICAL; REAL, REAL(4); DOUBLE PRECISION, REAL(8); COMPLEX, COMPLEX(4); DOUBLE COMPLEX, COMPLEX(8);
•
избегайте использования медленных арифметических операций. Перечислим операции в порядке снижения их эффективности: • сложение (+), вычитание (-), умножение чисел с плавающей точкой (*); • целочисленное умножение (*); ⎯ 204 ⎯
6. Повышение быстродействия программ
деление (/); возведение в степень (**). Так, вместо y = x ** 2 лучше записать y = x * x. Вместо y = x / 2.0 записать y = 0.5 * x; • •
•
заменяйте оператор y = x ** 0.5 на оператор, использующий встроенную функцию извлечения квадратного корня: y = SQRT(x);
•
не используйте выражения с разными типами данных, например:
real(4) :: x, y y=2*x константу 2.0 y = 2.0 * x
•
!
Вместо
константы
2
используйте
! Это лучше, чем 2 * x
выносите за пределы цикла не зависящие от его параметров выражения, например вместо фрагмента
do i = 1, n array(i) = 2.0 * float(i) * sqrt(array2(i)) * x end do
используйте фрагмент do i = 1, n array(i) = float(i) * sqrt(array2(i)) end do array = 2.0 * x * array
•
! Выносим 2.0 * x за пределы цикла
заменяйте внешние процедуры на внутренние, поскольку такие процедуры легче сделать встроенными, т. е. заменить вызовы процедур на их код.
6.5. Влияние опций команды DF на производительность Рассмотрим варианты создания объектных и исполняемого файлов на базе главной программы, размещенной в файле example.f90 и использующей процедуры, размещенные в файлах find_ab.f90 и find_cd.f90. Для построения исполняемого файла используем команду DF. На производительность применяемой программы влияет опция команды DF /optimize:уровень. Причем на ранних стадиях работы над программой с целью ускорения компиляции и построения EXE-файла применяется низкий уровень оптимизации либо оптимизация не используется вовсе. Например: ⎯ 205 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
DF /compile_only /debug /optimize:1 find_ab.f90 DF /compile_only /debug /optimize:1 find_cd.f90 DF /exe:example /debug /optimize:0 example.f90 find_ab.obj find_cd.obj Первые две команды создают объектные файлы find_ab.obj и find_cd.obj. Вторая компилирует файл с главной программой example.f90 и строит исполняемый файл example.exe. Замечание. Задание опции /debug без параметров меняет устанавливаемый по умолчанию уровень оптимизации с /optimize:4 на /optimize:0, т. е. отключает какую-либо оптимизацию. После завершения отладки результирующий EXE-файл следует построить, используя четвертый (задается по умолчанию) уровень оптимизации, удалив при этом опцию /debug или заменив ее на опцию /nodebug (/debug:none), например: DF /compile_only /nodebug /optimize:4 find_ab.f90 DF /compile_only /nodebug /optimize:4 find_cd.f90 DF /exe:example /debug:none example.f90 find_ab.obj find_cd.obj Замечание. Отсутствие опции /debug равнозначно заданию минимального уровня отладочной информации - /debug:minimal или /Zd. Приведенная последовательность создания исполняемого файла не является наилучшей, поскольку исходные файлы компилируются раздельно. Совместное компилирование исходных файлов и одновременное построение EXE-файла улучшает качество оптимизации, поскольку: •
существует больше возможностей для встраивания процедур. При встраивании процедуры вместо ее вызова в теле вызывающей программной единицы размещается код вызываемой процедуры (если такая замена возможна). Поскольку встраивание ведет к росту размера исполняемого файла, то, реализуя этот процесс, компилятор решает задачу оптимизации кода как с позиции быстродействия, так и по критерию его объема;
•
более полно анализируются потоки данных;
•
может сократиться число внешних процедур, поиск которых должен выполнить построитель. Пример более оптимального варианта команды DF: DF /exe:example /debug:none example.f90 find_ab.f90 find_cd.f90 Замечание. По умолчанию в случае опции /debug:none уровень оптимизации /optimize:4. ⎯ 206 ⎯
6. Повышение быстродействия программ
При использовании опции /compile_only, когда команда DF создает только объектные файлы и не выполняет построение EXE-файла, используйте также и опцию /object[:имя файла], обеспечивающую создание единого объектного файла для всех компилируемых файлов, например: DF /compile_only /debug:none example.f90 find_ab.f90 find_cd.f90 /object:example.obj DF /exe:example /debug:none example.obj Создавая единый объектный файл, компилятор имеет возможность выполнить совместный анализ обрабатываемых файлов и, следовательно, создать более оптимальный по производительности код. И напротив, не следует компилировать несколько исходных файлов одной командой DF, в которой присутствует опция /compile_only и опущена опция /object, например: DF example.f90 find_ab.f90 find_cd.f90 /compile_only Это не рекомендуется, поскольку, во-первых, будет создано несколько объектных файлов (для данного примера - 3), а во-вторых, серьезно сокращаются возможности для оптимизации кода. Об этом, кстати, компилятор проинформирует составляющего программу пользователя: df: info: Some interprocedural optimizations may be disabled when compiling in this mode Если все же по причине большого числа файлов необходимо использовать несколько DF-команд, то в каждой из них следует объединить связанные между собой исходные файлы. В табл. 6.2 приведены опции команды DF, влияющие на производительность получаемого в результате компиляции и построения исполняемого файла. Таблица 6.2. Опции компилятора, влияющие на производительность программ Опция
/align:ключевое слово
Описание
Управляет добавлением пустых ячеек памяти между данными в common-блоках и производных типах. Ячейки добавляются для того, чтобы обеспечить естественное выравнивание данных в оперативной памяти
⎯ 207 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
/fast
С целью повышения производительности устанавливает следующие опции компилятора: /align:dcommons, /assume:noaccuracy_sensitive и /math_library:fast
/assume:noaccuracy_sensitive
Позволяет компилятору изменять порядок выполнения арифметических операций с целью повышения производительности программы. Может, однако, привести к некоторой потери точности вычислений
/inline:all
Встраивает, если это возможно, каждый вызов процедуры. Чтобы избежать бесконечных рекурсий, не встраиваются некоторые рекурсивные процедуры
/inline:speed
Встраивает, если это возможно, большинство вызовов, не обращая внимания на размер получаемого исполняемого файла
/inline:size
Встраивает, если встраивание возможно, только те вызовы, которые не приводят к существенному росту размера исполняемого файла
/math_library:fast
Ускоряет вызовы встроенных процедур библиотеки за счет того, что не выполняется проверка передаваемых в процедуры параметров. Может привести к снижению точности и уменьшению надежности контроля за арифметическими исключениями
/optimize:уровень
Управляет уровнем оптимизации. По умолчанию, если не задана опция /debug, компиляция выполняется с опцией /optimize:4, в противном случае - с /optimize:0
/unroll:count (0 ≤ count ≤ 16)
Задает, когда действует опция /optimize:3 или 4, число раскруток цикла. При раскрутке (если циклы вложенные, то раскручивается внутренний цикл) создаются копии тела цикла таким образом, чтобы могла быть выполнена эффективная конвейерная обработка этих копий. Предельное число создаваемых копий определяется параметром count опции /unroll. Если опция /unroll:count опущена, то оптимизирующий компилятор раскручивает большинство циклов 4 раза и некоторые циклы 2 раза
⎯ 208 ⎯
6. Повышение быстродействия программ
В табл. 6.3 приведены опции компилятора, использование которых может снизить производительность генерируемых приложений. Таблица 6.3. Опции компилятора, снижающие производительность программ Опция
Описание
/assume:dummy_aliases
Заставляет компилятор создавать объектный код, в котором формальные параметры процедур разделяют память с другими формальными параметрами либо с переменными, передаваемыми посредством useассоциирования или common-блоков. Устаревший прием, не используемый в современном Фортране
/compile_only или /c
Производительность может понизиться, если при использовании этой опции создавать не один, а несколько объектных файлов. Поэтому эту опцию используйте совместно с опцией /object
/check:bounds
Генерирует дополнительный код, выполняющий в процессе работы программы проверку границ массивов и строк. При нарушении границ возникает ошибка исполнения
/check:overflow
Генерирует дополнительный код, выполняющий в процессе применения программы проверку целочисленной арифметики на предмет превышения допустимых для используемых разновидностей типов значений. Об обнаруженных нарушениях выдаются сообщения. После отладки удалите эту опцию (если она применялась). Это приведет и к снижению размера исполняемого файла, и к повышению производительности
/fpe:уровень
Контролирует во время выполнения исключения в операциях с плавающей точкой, что приводит, с одной стороны, к повышению надежности программы, а с другой - к понижению производительности. По умолчанию в DVF под Windows применяется опция /fpe:3. Использование другого уровня ведет к снижению производительности
/debug:ключевое слово
Генерирует дополнительную таблицу с отладочной информацией
⎯ 209 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
/inline:none /inline:manual
Предотвращает встраивание процедур; исключение составляют операторные функции
/optimize:0, или /optimize:1, или /optimize:2, или /optimize:3
Задание меньше максимально возможного уровня оптимизации программы (/optimize:4) снижает ее производительность. Такие опции используются на этапе отладки. При построении предназначенного для эксплуатации исполняемого файла используйте /optimize:4
/vms
Обеспечивает генерацию кода, пригодного для использования в OpenVMS VAX-системе. Устанавливает опцию /align:norecords, т. е. для выравнивания потребуется применения опции /align:records. По умолчанию действует опция /novms
6.6. Обобщения Производительность созданной вами программы вырастет, если вы: •
будете разрабатывать быстрые алгоритмы и использовать эффективные структуры данных;
•
освоите и станете правильно применять опции команды DF (опции компилятора и построителя), влияющие на скорость вычислений;
•
создадите и будете придерживаться стиля программирования, который учитывает все факторы, влияющие на скорость исполнения программы. В быстрой программе:
•
используются современные и не используются устаревшие (например, ассоциирование памяти) методы программирования;
•
данные в оперативной памяти выравнены естественным образом;
•
правильно организована работа с массивами и с циклами, в которых выполняется обработка массивов;
•
применены оптимальные приемы В/В данных и организации внешних файлов;
•
учтены и по необходимости реализованы рекомендации, приведенные в разд. 6.4.3.
⎯ 210 ⎯
7. Программирование на нескольких языках 7.1. Введение В 32-битовых приложениях DVF одновременно могут содержаться процедуры, написанные на четырех языках: Фортране, СИ/СИ++, Бейсике и Ассемблере. При работе с разноязычными процедурами должны соблюдаться соглашения о вызовах, включающих соглашения об именах процедур, их параметрах и способе поддержания стека. Возможны, кроме задаваемого по умолчанию, 2 соглашения о вызовах: C и STDCALL. Рассматриваемые далее соглашения заведомо действуют при работе с языками Фортран фирм Digital и Microsoft, СИ, СИ++ (Visual С++), Бейсик (Visual Basic) и Ассемблер фирмы Microsoft. Договариваться об используемых именах необходимо, так как в СИ значим регистр имен. Кроме того, имена процедур, генерируемые компилятором, могут отличаться от имен, присутствующих в исходном коде. Например, размещенная в файле d.f90 подпрограмма subroutine find_cd(c, d) real(4) :: c, d c = 3.0; d = 4.0 end subroutine find_cd
после компиляции посредством команды DF /c d.f90 будет представлена в объектном файле именем _FIND_CD@8. Размещенный в файле cmain.c прототип написанной на Фортране подпрограммы find_cd void main( ) { float c, d; // Прототип Фортран-подпрограммы find_cd extern void find_cd(float *c, float *d); find_cd(&c, &d); // Вызов Фортран-подпрограммы find_cd printf("%6.2f, %6.2f", c, d); // 3.0 4.0 }
после компиляции, выполненной командой CL /c cmain.c, получит в файле cmain.obj внешнее имя _find_cd. Ясно, что из-за разницы имен, не удастся вызвать подпрограмму find_cd из СИ-функции. Выход из положения изменить внешнее, используемое при вызове, имя процедуры find_cd либо в ⎯ 211 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Фортране, либо в СИ. Выполним это в СИ, применив соглашение STDCALL: extern void __stdcall FIND_CD(float *c, float *d); FIND_CD(&c, &d); // Внешнее имя функции - _FIND_CD@8
Тогда компилятор даст ей, функции, имя _FIND_CD@8, т. е. Фортрани СИ-имена согласованы и, значит, вызов подпрограммы Фортрана find_cd из СИ-функции main возможен. Соглашения о параметрах устанавливают, каким образом и в каком порядке передаются параметры процедуре: по значению или ссылке, слева направо или наоборот. Например, если задана СИ-функция void find_ab(float *a, float *b) { *a = 1.0; b = 2.0; }
то при ее вызове из Фортран-программы параметрами функции должны быть не имена переменных, а их адреса, возвращаемые встроенной функцией LOC: call find_ab(loc(a), loc(b))
Второй способ передачи параметров по ссылке - использование атрибута REFERENCE. В части стека соглашения рассматривают 2 аспекта: получает ли процедура фиксированное или переменное число параметров и какая процедура очищает стек после вызова (вызывающая или вызываемая). При составлении программ можно использовать любое из соглашений (задаваемое по умолчанию, C и STDCALL). Можно в одной и той же программе использовать все соглашения одновременно: они в известной степени взаимозаменяемы. Однако проще и понятнее работать с одним соглашением. Замечание. Взаимозаменяемость соглашений не полная. Например, атрибут VARYING, позволяющий осуществлять вызовы с переменным числом параметров, можно применять только при работе с соглашением C. На практике при вызове написанной на ином языке процедуры необходимо решить две задачи: •
выбрать соглашение и установить интерфейсы между вызываемыми разноязычными процедурами;
•
учесть особенности совместного использования различных объектов данных: строк, массивов, указателей и т. д.
⎯ 212 ⎯
7. Программирование на нескольких языках
Приведем в табл. 7.1 для справки способы объявления функций и процедур, применяемые в четырех рассматриваемых языках. Напомним, что функция, в отличие от подпрограммы, возвращает в качестве результата некоторое значение, которое используется в выражении, из которого функция вызвана. Таблица 7.1. Объявление процедур в Фортране, СИ, Бейсике и Ассемблере Язык
Функция
Подпрограмма
Фортран
FUNCTION
SUBROUTINE
СИ
void
Бейсик
Function
Sub
Ассемблер
PROC
PROC
Замечание. Вызов процедур Бейсика из Фортрана, СИ и Ассемблера невозможен. В то же время вызов из Бейсика процедур СИ и Фортрана допустим.
7.2. Атрибуты DEC Взаимодействие разноязычных программ поддерживается атрибутами DEC, которые являются расширением DVF по отношению к стандарту Фортрана. Эти атрибуты придают объектам DVF (данным и процедурам) дополнительные свойства, благодаря которым становится возможным соединение разноязычных процедур, например написанных на Фортране и СИ. Перечень атрибутов DEC и объектов, к которым они могут быть применены, дан в табл. 7.2. Таблица 7.2. Атрибуты DEC Атрибут
Объявление переменных
Оператор COMMON
Операторы определения процедур и оператор EXTERNAL
ALIAS
Нет
Да
Да
C
“
“
“
DLLEXPORT
“
“
“
DLLIMPORT
“
“
“
⎯ 213 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
EXTERN
С глобальными переменными
Нет
Нет
REFERENCE
С формальными параметрами
“
“
STDCALL
Нет
Да
Да
VALUE
С формальными параметрами
Нет
Нет
VARYING
Нет
“
Да
Атрибуты DEC вводятся метакомандой !DEC$ATTRIBUTES; при задании нескольких атрибутов они разделяются запятой, например: !DEC$ATTRIBUTES STDCALL, ALIAS : '_find_ab@8' :: find_ab
Атрибуты DEC могут быть использованы при определении процедур и при объявлении типов данных с операторами INTERFACE и ENTRY. Действие атрибутов передается в программные единицы и через useассоциирование.
7.2.1. Атрибут ALIAS Атрибут задает внешнее имя (псевдоним) процедуры (подпрограммы или функции) или общего блока данных (common-блока). Синтаксис: ALIAS : внешнее имя внешнее имя - символьная константа (заключенная в кавычки последовательность символов; может быть СИ-строкой). В отличие от имен Фортрана в имени ALIAS является значащим регистр букв. Так, ALIAS : 'PAT' и ALIAS : 'Pat' - разные имена. Для внешнего имени могут быть использованы недопустимые в Фортране, но приемлемые в ином языке имена, например имена, начинающиеся с символа подчеркивания. В исходном файле вызов процедуры может быть выполнен только по ее основному имени. В файле, содержащем код на другом языке, процедура может быть вызвана только по ее псевдониму. Атрибут ALIAS используется в операторе INTERFACE и в самой процедуре для переопределения ее внешнего имени. Атрибут ALIAS имеет больший приоритет, чем атрибуты STDCALL и С. Если ALIAS использован для процедуры, например, совместно с атрибутом С, то при вызове процедуры будут использованы применяемые в СИ соглашения, но внешнее имя процедуры по-прежнему будет определяться атрибутом ALIAS. ⎯ 214 ⎯
7. Программирование на нескольких языках
Атрибут не может быть использован с внутренними процедурами и формальными параметрами. Пример. В СИ-функции cfun подпрограмма iname будет вызываться по имени _ename. program fortmain interface subroutine cfun( ) ! Интерфейс СИ-функции cfun, которая вызывается !dec$attributes c,:: cfun ! в Фортран-программе по имени _cfun end subroutine cfun subroutine iname( ) ! Интерфейс подпрограммы iname, которая !dec$attributes c, alias: '_ename' :: iname! в СИ-функции cfun вызывается end subroutine iname ! по имени _ename end interface call cfun( ) ! Вызов СИ-функции cfun call iname( ) ! В Фортране вызов по-прежнему end program fortmain ! выполняется по имени iname subroutine iname !dec$attributes c, alias: '_ename' :: iname print *, 'Called' end subroutine iname void cfun( ) { iname extern void ename( ); ename( ); }
// СИ-функция, вызывающая подпрограмму // по имени _ename
Компиляцию файла cfun.c, содержащего функцию cfun, выполним командой CL /c cfun.c. Исполняемый файл fmain.exe получим командой DF fmain.f90 cfun.obj /nodebug, в которой fmain.f90 - имя файла с исходным текстом на Фортране.
7.2.2. Атрибуты С и STDCALL При использовании функций СИ, процедур Бейсика или Ассемблера совместно с Фортран-программами необходимо указать, каким образом выполняется передача данных. DVF поддерживает 2 соглашения о вызовах, задаваемых атрибутами C и STDCALL. Эти атрибуты применимы к процедурам и общим блокам. При использовании с процедурой атрибуты определяют набор соглашений о вызовах. Различия между используемыми соглашениями приведены в табл. 7.3. ⎯ 215 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Таблица 7.3. Соглашения о вызовах Соглашение
C
STDCALL
По умолчанию
Добавление к имени ведущего подчеркивания
Да
Да
Да
Добавление размера стека к имени процедуры
Нет
“
“
Чувствительность к регистру букв
Да
“
Нет
Обработка параметров справа налево
“
“
Да
Очистка стека
“
Нет
Нет
Переменное число параметров
Да
Нет
Нет
Передача длины для строки
Нет
“
Да
Передача параметров по значению*
Да
Да
Нет
* Имена массивов и строк в СИ являются указателями, что обеспечивает их передачу в СИ по ссылке.
Задаваемые по умолчанию и STDCALL соглашения добавляют код в вызываемую процедуру, который восстанавливает стек, когда завершаются вычисления в вызванной процедуре. Это обеспечивает генерацию меньших по размеру программ по сравнению с соглашениями C, при которых такой код следует за каждым вызовом процедуры. Это необходимо, поскольку соглашение C допускает вызов с переменным числом параметров; и первый параметр заносится в стек последним, поэтому он всегда располагается в вершине стека и его адрес не зависит от числа переданных параметров. Такая конфигурация стека позволяет, с одной стороны, вызывающей процедуре знать, сколько передано параметров и их реальные размеры, а с другой - осуществлять вызовы с переменным числом параметров. При вызове из Фортрана СИ-функции с переменным числом параметров используются одновременно атрибуты C и VARYING. По умолчанию параметры процедур, определенных с атрибутами C и STDCALL, передаются по значению. Однако их можно передать и по ссылке, применив атрибут REFERENCE. В процедуры, использующие стандартные соглашения Фортрана о вызовах, аргументы передаются по ссылке (при условии, если для них не указан атрибут VALUE). Имена, объявленные в процедурах Фортрана, использующих атрибут С, автоматически модифицируются: внешние имена переводятся на нижний регистр и предваряются знаком подчеркивания. Например, подпрограмма с ⎯ 216 ⎯
7. Программирование на нескольких языках
именем PROC, определенная с атрибутом C, независимо от числа параметров получит внешнее имя _proc. Для использования имен, содержащих заглавные буквы, необходимо применить атрибут ALIAS. Внешние имена в процедурах, использующих атрибут STDCALL, переводятся на нижний регистр, предваряются знаком подчеркивания и завершаются знаком @ и числом передаваемых параметрами байт. Например, подпрограмма с именем PROC, имеющая 4 параметра типа INTEGER(4) и определенная с атрибутом STDCALL, получит внешнее имя _proc@16. Атрибуты не могут быть применены к формальным параметрам за исключением параметров типа INTEGER. В СИ, Бейсике и Ассемблере также могут быть заданы соглашения о вызовах. Соответствие между соглашениями, применяемыми в этих языках, и соглашениями Фортрана приведены в табл. 7.4. Строку таблицы надо понимать так (на примере первой строки): если в CИ используется соглашение cdecl, которое, кстати, задается по умолчанию и может не объявляться явно, то в Фортране-процедуре следует использовать атрибут C. Таблица 7.4. Соответствие между соглашениями о вызовах Язык
Соглашение
Атрибут DEC
СИ
cdecl (задается по умолчанию)
C
СИ
__stdcall
STDCALL
Бейсик
По умолчанию
По умолчанию
Бейсик
CDECL (ключевое слово)
C
Ассемблер
C (в объявлениях PROTO и PROC)
C
Ассемблер
STDCALL (в объявлениях PROTO и PROC)
STDCALL
Примеры вызовов СИ-функции find_ab, размещенной в файле cfun.c, из Фортран-программы, находящейся в файле fmain.f90: 1. Используем соглашение cdecl, поэтому интерфейс функции find_ab в Фортран-программе должен содержать атрибут C. Функции find_ab в процессе компиляции будет присвоено внешнее имя _find_ab. void cdecl find_ab(float *a, float *b) { *a = 1.0; *b = 2.0; } program simple1 interface
// Квалификатор cdecl может быть опущен
! Интерфейс СИ-функции find_ab
⎯ 217 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
subroutine find_ab(ia, ib) !dec$attributes с :: find_ab integer(4) :: ia, ib программе end subroutine find_ab end interface real(4) a, b a = 10.0; b = 20.0 call find_ab(loc(a), loc(b)) функции print '(2f7.2)', a, b end program simple1
! _find_ab - внешнее имя функции find_ab ! ia, ib - адреса объявленных в главной ! вещественных переменных a, b
! Вызов по ссылке; нужен, так как в СИ! использованы указатели
2. Используем в СИ соглашение __stdcall, следовательно, интерфейс функции find_ab в Фортран-программе должен содержать атрибут STDCALL. Функции find_ab будет дано внешнее имя _find_ab@8. void __stdcall find_ab(float *a, float *b) { *a = 1.0; *b = 2.0; // Квалификатор __stdcall задается явно } program simple2 interface subroutine find_ab(ia, ib) find_ab !dec$attributes stdcall :: find_ab integer(4) :: ia, ib программе end subroutine find_ab end interface real(4) :: a, b a = 10.0; b = 20.0 call find_ab(loc(a), loc(b)) print '(2f7.2)', a, b end program simple2
! Интерфейс СИ-функции find_ab ! _find_ab@8 - внешнее имя функции ! ia, ib - адреса объявленных в главной ! вещественных переменных a, b
! Вызов по ссылке, так как в СИ-функции ! использованы указатели
Компиляция файла cfun.c выполняется командой CL /c cfun.c, а получение исполняемого файла go.exe - командой DF fmain.f90 cfun.obj /exe:go.exe.
7.2.3. Атрибут EXTERN Атрибут указывает, что переменная, объявленная в другом файле, написанном на отличном от Фортрана языке программирования, является
⎯ 218 ⎯
7. Программирование на нескольких языках
глобальной. Атрибут не может быть использован для формальных параметров процедур. Глобальные переменные могут быть объявлены в СИ и Ассемблере. В Фортране аналогом глобальных переменных являются объекты, объявленные в операторе COMMON; для доступа к этим объектам в другом языке используется имя common-блока. В Бейсике глобальные переменные не используются, и обмен данными между процедурами Фортрана и Бейсика выполняется через их параметры. Объявление глобальных переменных: •
в СИ глобальные переменные объявляются вне функций, например:
int glovar1; void cfun( ) { glovar1 = 4; }
// glovar1 - глобальная переменная СИ
•
в Ассемблере - оператором PUBLIC, имеющим синтаксис: PUBLIC [соглашение] имя глобальной переменной соглашение - это C или STDCALL. Опция соглашение, если она присутствует в операторе PUBLIC, перекрывает действие соглашения, заданного директивой .MODEL;
•
в Ассемблере заданную в Фортране глобальную переменную объявляют так: EXTRN [соглашение] имя глобальной переменной
•
в Фортране глобальная переменная pival - имя common-блока - задается так:
!dec$attributes alias : '_pival' :: pival pival real(4) :: pi common/pival/ pi pi = 2.0 * asin(1.0)
•
! _pival - внешнее имя глобального имени
в Фортране глобальная переменная glovar1, заданная в СИ, объявляется так:
! Атрибуты EXTERN и ALIAS обязательны; атрибут C может быть опущен !dec$attributes c, extern, alias : '_glovar1' :: glovar1
Задача. Распечатать значение объявленной выше глобальной переменной СИ glovar1 в Фортран-программе. А в СИ-функции вывести значение объявленной в common-блоке Фортрана переменной pi. ⎯ 219 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Пусть СИ-код размещен в файле cfun.c, а текст Фортран-программе - в файле fmain.f90. Компиляцию СИ-файла выполним командой CL /c cfun.c, а исполняемый файл fmain.exe создадим командой DF fmain.f90 cfun.obj /nodebug. ! Программа, вызывающая СИ-функцию cfun и выводящая значение глобальной переменной program showglovar1 ! Текст храним в файле fmain.f90 !dec$attributes c, extern, alias : '_glovar1' :: glovar1 !dec$attributes alias : '_pival' :: pival ! _pival - внешнее имя common-блока pival integer(4) :: glovar1 ! Исполняемый файл получим командой interface ! DF fmain.f90 cfun.obj /nodebug subroutine cfun( ) ! Атрибут ALIAS может быть опущен !dec$attributes c, alias : '_cfun' :: cfun end subroutine cfun end interface real(4) :: pi common/pival/ pi ! Глобальным является имя common-блока pi = 2.0 * asin(1.0) call cfun( ) ! pi = 3.141593 print *, glovar1 ! 4 end program showglovar1
Внешнее имя _pival common-блока можно задать, применив вместо атрибута ALIAS атрибут С: !dec$attributes c :: pival
Текст СИ-функции: int glovar1; void cfun( ) { extern struct { float pi; pival } pival; glovar1 = 4; printf("\npi = %9.6f\n", pival.pi); }
// glovar1 - глобальная переменная СИ // common-блок pival в СИ объявляется // как структура. Доступ к common-блоку // выполняется по внешнему имени _pival // Вывод заданной в Фортране глобальной // переменной pi
7.2.4. Атрибут REFERENCE Атрибут используется только для формальных параметров и обеспечивает их передачу по ссылке, а не по значению. Может быть использован как для отдельных формальных параметров, так и с именем
⎯ 220 ⎯
7. Программирование на нескольких языках
процедуры. В последнем случае все ее параметры будут передаваться по ссылке. При передаче по ссылке формальный параметр адресует фактический параметр, а не его копию. Поэтому изменение в процедуре значения формального параметра приведет к изменению значения фактического параметра. По умолчанию в Фортране параметры-переменные передаются по ссылке.
7.2.5. Атрибут VALUE Атрибут обеспечивает передачу параметров по значению. В этом случае формальный параметр получает копию фактического. Фактический параметр при передаче по значению останется без изменений, даже если в процедуре изменено значение соответствующего формального параметра. Это обусловлено тем, что формальный параметр адресует не фактический параметр, а его копию. Когда формальный параметр имеет атрибут VALUE, то передающий ему значение фактический параметр может быть другого типа. Если необходимо преобразование типа, то оно выполняется до вызова. Если при определении Фортран-процедуры задан атрибут C, то все параметры, кроме массивов, строки и ссылок, принимают атрибут VALUE. Строки, массивы и ссылки не могут быть переданы по значению, и, следовательно, с ними не должен использоваться атрибут VALUE. Пример: program ref_var_test integer(4) :: kr = 2, kv = 2 write(*, *) kr, kv call refval(kr, kv) write(*, *) kr, kv end program ref_var_test subroutine refval(kr, kv) !dec$attributes reference :: kr !dec$attributes value :: kv integer(4) :: kr, kv kr = kr + 2 kv = kv + 2 end subroutine refval
!
2
2
!
4
2
! Параметр kr передается по ссылке ! Параметр kv передается по значению ! Измененное значение формального ! параметра не передается фактическому
⎯ 221 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
7.2.6. Атрибут VARYING Атрибут используется только для подпрограмм и функций, объявленных с атрибутом С. Задание атрибута VARYING позволяет обращаться к процедуре, например к СИ-функции, с разным числом параметров. При этом число фактических (передаваемых) параметров не должно превышать число формальных параметров. Задача. Организовать из Фортрана вызов функции Ассемблера с переменным числом параметров. ! Используем соглашение C с атрибутом VARYING program fortmain ! Код храним в файле fmain.f90 interface function sumcv(nterms, term) !dec$attributes c, varying :: sumcv ! Внешнее имя функции - _sumcv integer(4) :: sumcv, nterms, term ! term - первый переданный параметр end function sumcv end interface integer(4) :: a = 1, b = 2, c = 3 print *, 'Sum of 0 terms is ', sumcv(0) ! 0 print *, 'Sum of 1 terms is ', sumcv(1, a) ! 1 print *, 'Sum of 2 terms is ', sumcv(2, a, b) ! 3 print *, 'Sum of 3 terms is ', sumcv(3, a, b, c) ! 6 end program fortmain ; Процедура Ассемблера, суммирующая заданное число целочисленных параметров .386 .MODEL FLAT, C sumcv PROTO C, nterms:SDWORD, term:VARARG .CODE ; nterms - число суммируемых параметров ; term - последовательность суммируемых параметров sumcv PROC C USES ebx, nterms:SDWORD, term:VARARG sub eax, eax ; Загружаем сумму из нуля параметров sub ebx, ebx ; Очищаем базовый регистр mov ecx, nterms ; Загружаем число переданных параметров .WHILE ecx > 0 add eax, term[ebx] ; Прибавляем очередной параметр dec ecx ; Уменьшаем число неиспользованных параметров add ebx, 4 ; Переходим к следующему параметру .ENDW done: ret ; Оставляем результат в регистре EAX
⎯ 222 ⎯
7. Программирование на нескольких языках
sumcv ENDP END
Поскольку вызов с разным числом параметров предусмотрен стандартом Фортрана, то атрибут VARYING может использоваться только для вызова процедуры из программы, написанной на другом языке, например СИ. В DVF процедура с таким атрибутом, если она написана на Фортране, вызвана быть не может: форма списков аргументов для атрибута VARYING несовместима с формой, поддерживаемой стандартом Фортрана. Напомним, что вызов с переменным числом параметром обеспечивается атрибутом OPTIONAL. Однако в FPS1 (Fortran PowerStation фирмы Microsoft версии 1) вызовы с атрибутом VARYING возможны, но процедура должна сама определять число полученных параметров. Для этого один из передаваемых параметров должен содержать информацию об их общем числе. Использование ключевых слов не поддерживается. Фактические и формальные параметры должны быть совместимы по порядку следования и типам. Пример. Компиляция программы выполнена в среде FPS с опцией /4fps1. В DVF эта опция игнорируется. program testvar interface to subroutine varpar [varying, c] (k, a, n) integer k, a(*), n ! k - число передаваемых параметров end integer a(10) /1, 2, 3, 4, 5, 6, 7, 8, 9, 10/ call varpar(2, a, 6) ! Передаем 2 параметра call varpar(1, a) ! Передаем 1 параметр end subroutine varpar(k, a, n) INTERFACE TO integer a(*), k, n, i if(k .eq. 1) n = 3 write(*, *) (a(i), i = 1, n) end program testvar
!
Атрибуты
указаны
в
операторе
7.2.7. Атрибуты DLLEXPORT и DLLIMPORT Атрибуты DLLEXPORT и DLLIMPORT задают интерфейс к динамически подключаемым библиотекам. Эти же атрибуты могут быть применены и к именам common-блоков. DLLEXPORT объявляет, что процедура или данные (имена общих блоков) экспортируются в другие приложения или динамические ⎯ 223 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
библиотеки DLL. При использовании этого атрибута компьютер создает наиболее эффективный код и нет необходимости задавать файл определения (DEF-файл) для экспорта символов. Задание атрибута DLLEXPORT должно выполняться в той же программной единице, где объявляются процедуры или данные, к которым атрибут применяется. Программа, использующая определенные в DLL символы, импортирует их. В такой программе, если она написана на Фортране, следует объявить атрибут DLLIMPORT. Объявление выполняется в той программной единице, которая импортирует символы. Пример: subroutine arraytest(arr) !dec$attributes dllexport :: arraytest real(4) :: arr(3, 7) integer(4) :: i, j do i = 1, 3 do j = 1, 7 arr(i, j) = 11.0 * i + j end do end do end subroutine arraytest
! Атрибут обеспечивает создание LIB ! и EXP файлов, нужных при генерации ! приложения, использующего DLL
Чтобы включить подпрограмму в динамическую библиотеку, необходимо создать DLL-проект. Для этого используется цепочка File New - Project Workspace - Dynamic-Link-Library - внести имя проекта в поле Name - Create. Пусть имя проекта fdll. Затем, применяя цепочку Insert - Files Into Project, добавим в проект файл, например arr.f90, содержащий текст подпрограммы arraytest. Таким же образом в проект добавляются и другие исходные файлы. После этого создадим DLL-библиотеку: Build - Build fdll.dll. Заметим, что одновременно с динамической библиотекой fdll.dll будет создан и файл fdll.lib, содержащий код, необходимый для организации ссылок на DLL. Пусть файлы библиотеки fdll.lib и fdll.dll размещены в папке fdll. Для использования динамической библиотеки fdll.dll в программе необходимо до генерации содержащего эту программу проекта добавить в опции компоновщика строку с именем библиотеки fdll.lib. Это можно сделать так: Build - Settings - Link - выбрать категорию General - добавить в поле Object/Library modules после библиотеки kernel32.lib строку "c:\fdll\fdll.lib". (Разделителем между именами библиотек является пробел.) Затем сгенерировать проект. Пусть результатом построения проекта является файл ted.exe. При запуске программы ted.exe, использующей библиотеку ⎯ 224 ⎯
7. Программирование на нескольких языках
fdll.dll, ей должен быть известен путь к файлу fdll.dll. Например, файл fdll.dll можно разместить там же, где находится файл ted.exe. program ted !dec$attributes dllimport :: arraytest ! Определяем имя импортируемого символа integer(4) :: i, j real(4) :: arr(3, 7) call arraytest(arr) ! Подпрограмма будет взята из fdll.dll print '(1x, 7f5.1)', ((arr(i, j), j = 1, 7), i = 1, 3) end program ted
7.3. Соглашения об именах Компилятор в зависимости от используемого соглашения и заданного атрибутом ALIAS имени name преобразовывает при компиляции это имя. Имя name может обозначать имя процедуры, переменной, common-блока или модуля. Правила преобразований имен сведены в табл. 7.5, в которой n равен размеру области стека в байтах, занимаемой параметрами процедуры. Таблица 7.5. Соглашения об именах в Фортране, СИ, Бейсике и Ассемблере Язык, соглашение
Имя после компиляции
Регистр букв имени в объектном файле
Фортран, C
_name
Строчные буквы
Фортран, STDCALL
_name@n
Фортран, по умолчанию
_name@n
Прописные буквы
СИ, cdecl или по умолчанию
_name
Прописные и строчные буквы
СИ, __stdcall
_name@n
VISUAL C++
?name@@decoration
”
Ассемблер, С (в PROTO и PROC)
_name
”
Ассемблер, STDCALL (в PROTO и PROC)
_name@n
”
”
“
То же
Замечание. В Фортране регистр важен лишь при задании имени в атрибуте ALIAS. Пример использования приведенной в таблице информации: пусть СИфункция задается кодом ⎯ 225 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
#include <math.h> float cname(float x, float y) { return sqrt(x * x + y * y); }
// Для встроенной функции sqrt // Параметры передаются по значению
Тогда, поскольку соглашение не задано явно и, следовательно, используется соглашение cdecl, для вызова этой функции из Фортрана при объявлении ее интерфейса следует применить атрибут C: program demo interface function cname(x, y) !dec$attributes c :: cname компилятор real(4) :: cname, x, y end function cname end interface print *, cname(3.0, 4.0) _cname end program demo
! Применение атрибута С означает, что ! даст функции cname внешнее имя _cname ! Это имя можно наблюдать в OBJ-файле ! Фактически вызов выполняется по имени
Вызов СИ-функции, как это следует из вышеприведенной таблицы, выполняется по имени _cname: именно в такое имя преобразовывают имя cname компиляторы СИ и Фортрана. В принципе можно изменить, применив атрибут ALIAS, имя, которое используется в Фортране для обращения к СИ-функция cname, например так: program demo2 interface function cname2(x, y) !dec$attributes c, alias : '_cname' :: cname2 real(4) :: cname2, x, y end function cname2 end interface ! Хотя в программе используется имя cname2, print *, cname2(3.0, 4.0) ! фактически вызов СИ-функции cname end program demo2 ! выполняется по внешнему имени _cname
Атрибут ALIAS, имея больший приоритет, чем атрибут C, даст указание компилятору преобразовать имя cname2 во внешнее имя _cname. Однако ясно, что такое применение атрибута ALIAS усложняет восприятие программы.
⎯ 226 ⎯
7. Программирование на нескольких языках
Снабдим теперь СИ-функцию квалификатором __stdcall: #include <math.h> float __stdcall cname(float x, float y) { return sqrt(x * x + y * y); }
// Для вызова встроенной функции sqrt // СИ-компилятор даст функции // внешнее имя _cname@8
Тогда СИ-компилятор даст функции внешнее имя _cname@8, в котором 8 число байт, отводимых в стеке под параметры функции cname. В Фортране то же имя будет сгенерировано компилятором в программе: program demo3 interface function cname(x, y) !dec$attributes stdcall :: cname real(4) :: cname, x, y end function cname end interface print *, cname(3.0, 4.0) end program demo3 _cname@8
! Хотя в программе используется имя cname, ! фактически вызов СИ-функции cname ! выполняется по внешнему имени
7.4. Прописные и строчные буквы в именах 7.4.1. Имена из прописных букв При совместном использовании программ на Фортране, СИ и/или Ассемблере необходимость записывать в двух последних языках имена прописными буквами возникает в том случае, когда файл с кодом на Фортране откомпилирован по заданному по умолчанию соглашению, т. е. без атрибутов C и STDCALL. Тогда сгенерированные компилятором имена будут состоять из прописных букв и, чтобы использовать эти имена в СИ или Ассемблере, следует применить в этих языках соглашение STDCALL, а запись соответствующих имен выполнить прописными буквами. Для Бейсика, как и Фортрана, все равно, из каких букв, прописных или строчных, составлены имена. Пример. Пусть в Фортране задана функция fortfun, компиляция которой выполнена по заданному по умолчанию соглашению: function fortfun(x, y) !dec$attributes value :: x, y вызывается real(4) :: fortfun, x, y
! Текст функции запишем в файл ffun.f90 ! Параметры, поскольку функция ! из СИ, нужно передать по значению
⎯ 227 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
fortfun = sqrt(x * x + y * y) командой end function fortfun /debug:none
! Исполняемый файл ffun.exe создадим !
DF
ffun.f90
cmain.obj
/nodformain
Тогда компилятор даст ей имя _FORTFUN@8. Следовательно, СИфункция, вызывающая функцию fortfun, должна содержать такой ее прототип: void main( ) { // Функция записана в файл cmain.c // Прототип функции fortfun extern float __stdcall FORTFUN(float x, float y); printf("%7.2f", FORTFUN(3.0, 4.0)); // Объектный код получен командой } // CL /c cmain.c
Имя fortfun в СИ-функции main записано прописными буквами: FORTFUN. Описание прототипов функции fortfun: •
в Бейсике:
Declare Function FORTFUN Lib "ffun.dll" (ByVal x As Single, ByVal y As Single) As Single
•
в Ассемблере:
.MODEL FLAT, STDCALL FORTFUN PROTO STDCALL, x, y: PTR REAL4 ... FORTFUN PROC STDCALL, x, y: PTR REAL4
В Ассемблере можно использовать также опцию OPTION CASEMAP ALL, которая переводит буквы всех имен в прописные. Тогда ввод имени функции fortfun может быть выполнен на любом регистре.
7.4.2. Имена из строчных букв Если внешние имена в СИ или Ассемблере заданы строчными буквами, то в Фортране, в зависимости от используемого в другом языке соглашения, следует явно задать соглашение C или STDCALL. Эти соглашения, если имя не задано атрибутом ALIAS, автоматически переводят буквы имен в строчные. Например: void main( ) { // Функция записана в файл cmain.c extern float __stdcall fortfun(float x, float y); // Прототип функции fortfun printf("%7.2f", fortfun(3.0, 4.0)); // Объектный код получен } // командой CL /c c.main.c function fortfun(x, y)
! Текст функции запишем в файл ffun.f90
⎯ 228 ⎯
7. Программирование на нескольких языках
!dec$attributes stdcall :: fortfun real(4) :: fortfun, x, y fortfun = sqrt(x * x + y * y) end function fortfun
! Внешнее имя функции - _fortfun@8 ! Параметры, поскольку задан атрибут ! STDCALL, передаются по значению
7.4.3. Имена из смеси прописных и строчных букв Если внешние имена в СИ или Ассемблере одновременно содержат и прописные и строчные буквы, то в Фортране, помимо задания соглашения о вызове, внешнее имя следует определить атрибутом ALIAS, полностью скопировав в нем внешнее имя СИ или Ассемблера. Например: •
соглашение STDCALL:
void main( ) { // Функция записана в файл cmain.c extern float __stdcall FortFun(float x, float y); // Прототип функции fortfun ... function fortfun(x, y) ! Текст функции запишем в файл ffun.f90 ! Внешнее имя функции - _FortFun@8 !dec$attributes stdcall, alias : '_FortFun@8' :: fortfun real(4) :: fortfun, x, y ! Параметры, поскольку задан атрибут ... ! STDCALL, передаются по значению
•
соглашение C:
void main( ) { extern float FortFun(float x, float y); ...
// Функция записана в файл cmain.c // Прототип функции fortfun
function fortfun(x, y) ! Текст функции запишем в файл ffun.f90 !dec$attributes c, alias : '_FortFun' :: fortfun ! Внешнее имя функции - _FortFun real(4) :: fortfun, x, y ! Параметры, поскольку задан атрибут ... ! C, передаются по значению
7.4.4. Имена VISUAL C++ VISUAL C++ (далее - просто СИ++) использует те же соглашения о вызовах и те же механизмы передачи внешних данных, что и СИ. Однако внешние имена формируются по иным, отличным от действующих в СИ правилам: компилятор преобразовывает внешнее имя name в имя ?name@@decoration. Избавиться от декоративного окончания @decoration позволяет модификатор extern "C". Например, СИ-функцию, если она предназначена для вызова в Фортран-программе, следует объявить так: extern "C" { void find_ab(float *a, float *b) { *a = 1.0; *b = 2.0; // Компилятор преобразовывает имя find_ab }} // в имя _find_ab
⎯ 229 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
При отсутствии модификатора extern "C" сгенерированное компилятором имя может иметь вид: ?find_ab@@YAXPAM0@Z. В случае соглашения STDCALL прототип внешней, написанной на Фортране функции find_cd в СИ++ запишется так: extern "C" { void __stdcall find_cd(float *, float *); }
Модификатор extern "C" используется в СИ++ для изменения соглашений об именах как при вызове внешних, написанных, например, на Фортране процедур, так и записи функций СИ++, вызываемых из процедур, закодированных на другом языке (Фортране, Бейсике или Ассемблере). Если же код функции СИ++, вызываемой, например, из Фортранпрограммы, не использует extern "C" и не может быть изменен, то можно, применив атрибут ALIAS, согласовать имена СИ++ и Фортрана (учесть во внешнем имени Фортрана декоративное окончание). Однако такой подход следует использовать в крайнем случае, поскольку Microsoft не гарантирует сохранение схемы формирования декоративного окончания @decoration в последующих версиях СИ++. На применение extern "C" накладываются ограничения: •
нельзя использовать extern "C" с функциями-членами;
•
в случае перегружаемых функций extern "C" может быть задан лишь для одной из функций, объединенных под одним родовым именем.
7.5. Интерфейс внешней процедуры В Фортране общий вид интерфейса процедуры, написанной на отличном от Фортрана языке, таков: INTERFACE SUBROUTINE | FUNCTION имя процедуры([список формальных параметров]) [DEC-атрибуты процедуры] [DEC-атрибуты формальных параметров] [объявление формальных параметров] END SUBROUTINE | FUNCTION [имя процедуры] END INTERFACE Пример. Пусть из Фортрана вызывается СИ-функция: void __stdcall find_ab2(float *a, float *b, float g) { *a = 2.0 * g; *b = 3.0; // Параметры a и b переданы по ссылке } // Параметр g передан по значению
⎯ 230 ⎯
7. Программирование на нескольких языках
Ее интерфейс и вызов запишутся в Фортране так: program fortmain interface subroutine find_ab2(a, b, g) !dec$attributes stdcall :: find_ab2 !dec$attributes reference :: a, b !dec$attributes value :: g real(4) :: a, b, g end subroutine find_ab2 end interface real(4) :: a, b call find_ab2(a, b, -1.0) print '(1x, 2f6.2)', a, b end program fortmain
! Атрибут подпрограммы ! Параметры a и b передаются по ссылке ! Параметр g передается по значению
! Первые 2 параметра передаются по ссылке ! -2.00 3.00
В приведенном интерфейсе предложение с атрибутом VALUE, может быть опущено, поскольку в случае STDCALL параметры по умолчанию передаются по значению. Можно не использовать и атрибут REFERENCE, но в этом случае СИ-функции find_ab2 надо передать, используя встроенную функцию LOC, адреса переменных a и b. Тогда интерфейс и вызов СИ-функции find_ab2 в программе fortmain2 запишутся так: program fortmain2 interface subroutine find_ab2(ia, ib, g) !dec$attributes stdcall :: find_ab2 integer(4) :: ia, ib real(4) :: g end subroutine find_ab2 end interface real(4) :: a, b call find_ab2(loc(a), loc(b), -1.0) переменных print '(1x, 2f6.2)', a, b end program fortmain
! Атрибут подпрограммы ! ia, ib - адреса переменных a и b ! Параметр g передается по значению
!
Два
первых
параметра
-
адреса
! -2.00 3.00
7.6. Согласование типов данных Обмениваясь данными между разноязычными процедурами, согласовывайте разные типы данных так, как это регламентируется табл. 7.6.
⎯ 231 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Таблица 7.6. Эквивалентные типы данных Фортран
СИ
Бейсик
–
Ассемблер
INTEGER(1), LOGICAL(1)
char
SBYTE
INTEGER(2), LOGICAL(2)
short int
Integer
SWORD
INTEGER(4), LOGICAL(4)
int, long
Long
SDWORD
REAL(4)
float
Single
REAL4
REAL(8)
double
Double
REAL8
CHARACTER(1)
unsigned char
CHARACTER(*)
См. разд. 7.7.1.1
COMPLEX(4)
COMPLEX(8)
–
BYTE
struct complex4 { float real, imag; };
–
COMPLEX4 STRUCT 4 real REAL4 ? imag REAL4 ? COMPLEX4 ENDS
struct complex8 { float real, imag; };
–
COMPLEX8 STRUCT 8 real REAL8 ? imag REAL8 ? COMPLEX8 ENDS
Замечание. Пользовательские функции Фортрана типа COMPLEX размещают скрытый комплексный параметр в начале списка параметров. Вызов такой функции в СИ-функции должен содержать этот параметр в явном виде и использовать его для возврата результата. При описании прототипа комплексной функции Фортрана в СИ-функции следует для задания типа использовать void. Например: function fortcomp(z1, z2) соглашение complex(4) fortcomp, z1, z2 fortcomp = z1 + z2 end function fortcomp void main( ) { struct complex4 { float real, imag;
! Используем
заданное
по умолчанию
! Внешнее имя функции - _FORTCOMP@12 ! Параметры функции передаются по ссылке
// Описание комплексного типа данных
⎯ 232 ⎯
7. Программирование на нескольких языках
} z, z1 = {1.0, 2.0}, z2 = {3.0, 4.0}; extern void __stdcall FORTCOMP(struct complex4 *, struct complex4 *, struct complex4 *); FORTCOMP(&z, &z1, &z2); // z - возвращаемое функцией значение printf("z = (%6.2f, %6.2f)", z.real, z.imag); // z = (4.0, 6.0) }
7.7. Передача данных в программах с разноязычными процедурами Существует 3 возможности обмениваться процедурами, реализованными на разных языках: •
через параметры процедур;
•
через объявленные в модуле данные;
данными
между
•
посредством глобальных (в Фортране - общих) данных. Рассмотрим каждую из них. Замечание. Частично вопросы обмена глобальными рассмотрены выше, в разд. 7.2.3.
данными
7.7.1. Обмен данных через параметры процедур Сведения о том, как передать данные по значению или по ссылке при разных соглашениях, приведены в табл. 7.7. Таблица 7.7. Управление передачей данных по ссылке и значению Язык, атрибут
Фортран, по умолчанию Фортран, C или STDCALL Фортран, по умолчанию Фортран, C или STDCALL СИ/СИ++
Вид параметра
Скаляры “ Массивы “ Скаляры
Как передать по ссылке
Как передать по значению
По умолчанию
Использовать атрибут VALUE
Использовать атрибут REFERENCE
По умолчанию
По умолчанию
Нельзя передать по значению
”
Нельзя передать по значению
“
Передать адрес параметра
⎯ 233 ⎯
По умолчанию
О. В. Бартеньев. Visual Fortran: новые возможности
СИ/СИ++
Массивы
По умолчанию
Создать структуру, компонентом которой является массив
Бейсик
Любой
”
Использовать ключевое слово BYVAL
Ассемблер
“
“
Использовать обозначение PTR
По умолчанию
Замечания: 1. Производные типы данных, даже если они содержат массивы, являются скалярными объектами. 2. Передача строк Фортрана и его ссылок имеет особенности, которые будут рассмотрены ниже. Напишем теперь на Фортране подпрограмму fortsub и приведем варианты ее объявления в СИ, Бейсике и Ассемблере. subroutine fortsub(aref, bval) ffun.f90 !dec$attributes stdcall :: fortsub !dec$attributes reference :: aref !dec$attributes value :: bval aref = 2.0 * bval end subroutine fortsub
! Текст на Фортране разместим в файле ! Используем соглашение STDCALL ! Параметр aref передается по ссылке ! Параметр bval передается по значению
void main( ) { // Код на СИ разместим в файле cmain.c extern void __stdcall fortsub(float *, float); // Прототип процедуры fortsub float aref, bref = -1.0; fortsub(&aref, bref); // Объектный cmain.obj-файл получим printf("aref = %7.2f", aref); // командой CL /c cmain.c }
Файл cmain.exe сгенерирует команда DF cmain.obj ffun.f90 /nodformain /nodebug. В Бейсике при объявлении внешней Фортран-подпрограммы fortsub используем со вторым параметром bval, который передается по значению, ключевое слово BYVAL: Declare Sub fortsub Lib "ffun.dll" (aref As Long, ByVal bval As Long)
Для первого параметра действуют заданные по умолчанию правила: он будет передан по ссылке. ⎯ 234 ⎯
7. Программирование на нескольких языках
В Ассемблере по умолчанию параметры передаются по значению. Чтобы вызвать Фортран-подпрограмму fortsub из Ассемблера, следует при ее объявлении в PROTO и PROC первый передаваемый по ссылке параметр снабдить обозначением PTR: fortsub PROTO STDCALL, aref: PTR SDWORD, bval: SDWORD
Далее используем значение параметра bval, например: mov eax, bval параметра bval
; Помещаем в регистр EAX значение
При работе с параметром, переданным по ссылке, используем его адрес: mov ecx, aref aref mov eax, [ecx] aref
; Загружаем в регистр ECX адрес параметра ; Теперь в регистр EAX загружаем значение
7.7.1.1. Передача символьных данных Если параметром процедуры Фортрана является строка, то по умолчанию вместе с ней передается скрытая длина строки. Длина содержится в целочисленном, типа INTEGER(4), параметре, который передается по значению. Применяя атрибуты, можно изменить способ передачи строк. Как это происходит, отражено в табл. 7.8. Таблица 7.8. Влияние атрибутов на способ передачи строк через параметры процедур Параметр и его атрибут
Строка
Атрибуты процедуры
Способ передачи
По умолчанию
Передается по ссылке вместе с длиной строки, которая передается по значению
”
C или STDCALL
Первый символ преобразовывается в INTEGER(4) и передается по значению
”
C, REFERENCE или STDCALL, REFERENCE
Передается по ссылке вместе с длиной строки, которая передается по значению
По умолчанию*
Ошибка
Строка с атрибутом VALUE
⎯ 235 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
То же
C или STDCALL
Первый символ преобразовывается в INTEGER(4) и передается по значению
C, REFERENCE или STDCALL, REFERENCE
То же
Строка с атрибутом REFERENCE
По умолчанию
Передается по ссылке вместе с длиной строки, которая передается по значению
То же
C или STDCALL
Передается по ссылке без длины строки
C, REFERENCE или STDCALL, REFERENCE
То же
”
”
Из приведенной таблицы следует, что, передавая строки в разноязычных процедурах, следует либо объявлять строку без атрибута и использовать задаваемое по умолчанию соглашение, либо объявлять строку с атрибутом REFERENCE и использовать соглашение C или STDCALL с атрибутом REFERENCE или без него. В других случаях, кроме отмеченного звездочкой, передается лишь первый символ строки. Передавая строки из СИ в Фортран и обратно, следует помнить, что в СИ строка является одномерным массивом символов и завершается nullсимволом. То есть из Фортрана в СИ должны передаваться СИ-строки. Напомним, что СИ-буквальные константы Фортрана завершаются буквой С, например: "It's a C-string"C
! Буквальная СИ-константа Фортрана
Между строками Фортрана и СИ есть еще одно отличие: нумерация индексов в строках СИ-функций начинается с нуля, а в Фортране - с единицы. Так, имя первого элемента строки st из 20 символов в СИ - st[0], а в Фортране - st(1:1). Объявляется эта строка так: char st[20]; character(20) st Фортране
// Объявление строки из 20 символов в СИ ! Объявление строки из 20 символов в
Строки, как и любой массив СИ, являются указателями и, следовательно, будучи параметрами СИ-функции, передаются по ссылке.
⎯ 236 ⎯
7. Программирование на нескольких языках
Пример 1. Организуем вызов символьный параметр, из Фортрана.
СИ-функции
cstring,
имеющей
#include <string.h> // Сохраним код в файле cfun.c; объектный код void cstring(char *st) { // получим командой CL /c cfun.c printf("%*s\n", strlen(st), st); strcpy(st, "Updated in C function cstring"); } program fortmain interface subroutine cstring(st) !dec$attributes c :: cstring !dec$attributes reference :: st character(*) :: st end subroutine cstring end interface character(50) :: string = 'Initial value'C call cstring(string) print *, string end program fortmain
! Программу храним в файле fmain.f90 ! Исполняемый файл создадим командой ! DF fmain.f90 cfun.obj /nodebug ! Используем соглашение С ! Строка передается по ссылке без длины
! Используем в Фортране СИ-строку ! Updated in C function cstring
Пример 2. Выполнить вызов подпрограммы Фортрана с символьным параметром из СИ, использовав в Фортране задаваемое по умолчанию соглашение. subroutine fstring(st) ! Код храним в файле ffun.f90 character(*) st st = 'Updated in Fortran subroutine fstring'C end subroutine fstring #include <string.h> // Сохраним код в файле cmain.c; объектный код void main( ) { // получим командой CL /c cmain.c char st[50] = "Initial value"; extern void __stdcall FSTRING(char *st, int stlength); FSTRING(st, 50); // Передаем строку и ее длину printf("%*s\n", strlen(st), st); // Updated in Fortran subroutine fstring }
Построение приложения выполним командой DF cmain.obj ffun.f90 /nodformain /nodebug Замечание. Функции Фортрана, результирующая переменных которых имеет тип CHARACTER, размещают, подобно комплексным функциям, ⎯ 237 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
скрытый параметр в начале списка параметров. Этот параметр может одновременно содержать как адрес строки, так и ее длину. Вызов такой функции в СИ-функции должен содержать этот параметр в явном виде и использовать его для возврата результата. При описании прототипа символьной функции Фортрана в СИ-функции следует для задания типа использовать void. Однако, чтобы избежать путаницы, лучше не использовать вызовы символьных функций Фортрана из СИ, а выполнить их замену на подпрограммы, введя дополнительный параметр для возврата результата. Другие возможности обмена символьными данными - это применение common-блоков и модулей. В Бейсике, использующем подпрограмму Фортрана с символьными параметрами, строки должны передаваться по значению. Фактически Бейсик хранит строки как структуры, содержащие длину строки и ее адрес. При передаче строки по значению сразу осуществляется доступ к адресу строки, который и передается Фортрану. Пусть дана вызываемая из Бейсика подпрограмма Фортрана: subroutine fstring(st) character(50) st st = 'Initial Fortran string value' end subroutine fstring
Тогда в Бейсике подпрограмма Фортрана fstring объявляется и вызывается так: Declare Sub FSTRING Lib "flib.dll" (ByVAl bst as String) DIM bst As String * 50 ' Строка фиксированной длины CALL FSTRING(bst)
Ассемблер не добавляет ни длины строки, ни завершающего nullсимвола. Чтобы передать длину строки, используйте синтаксис lenstring BYTE "String with length", LENGTHOF lenstring
null-символ добавляется так: nullstring BYTE "Null terminated string", 0
7.7.1.2. Передача массивов При работе с массивами в разноязычном приложении следует учитывать, что:
•
в Ассемблере могут быть использованы только одномерные массивы;
•
в Фортране по умолчанию нижняя граница каждого измерения равна единице, а в СИ и Бейсике - нулю. Например, если задан вещественный массив x из 10 элементов и используются заданные по умолчанию ⎯ 238 ⎯
7. Программирование на нескольких языках
правила, то имя первого элемента массива в Фортране - x(1), в СИ - x[0], а в Бейсике - x(0). В Ассемблере для адресации первого элемента используется имя массива - x, для адресации следующего элемента надо переместиться на 4 байта, например так: x[4] - или так: x + 4. Объявления массив x: real(4) :: x(10) = 1.0 Фортране float x[10] = {1.0, 2.0, 3.0}; DIM x (1 to 10) As Single x REAL4 10 DUP(1.0) Ассемблере
! Объявление и инициализация массива в // Объявление и инициализация массива в СИ ' Объявление массива в Бейсике ; Объявление и инициализация массива в
В Бейсике можно изменить индексацию массива, начав ее с единицы, использовав в программе оператор Option Base 1
•
в Фортране и Бейсике элементы многомерного массива в памяти компьютера расположены иначе, чем в СИ: в двух первых языках при размещении элементов массива быстрее всего меняется левый индекс и медленнее всего правый; в СИ обратная картина: быстрее всего меняется правый индекс и медленнее всего левый. Так, двумерный массив a формы (3, 2) расположен в Фортране и Бейсике в памяти по столбцам, а в СИ - по строкам. Графически это иллюстрирует рис. 7.1. a(1, 1)
a(2, 1)
a(3, 1)
a(1, 2)
a(2, 2)
a(3, 2)
a[1][1]
a[2][0]
a[2][1]
a a[0][0]
a[0][1]
a[1][0]
б Рис. 7.1. Элементы массива в памяти компьютера: а - массив Фортрана; б - массив СИ
Согласование порядка следования элементов массива в Фортране (Бейсике) и СИ обеспечивается надлежащими объявлениями массива. Так, объявлению Фортрана integer(4), dimension(3, 2) :: a
соответствует объявление СИ int a[2][3];
Пример 1. Вызвать из СИ подпрограмму Фортрана ввода массива.
⎯ 239 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Используем соглашение STDCALL. Поскольку вызов выполняется из СИ, где массивы передаются по ссылке, то в подпрограмме Фортрана следует также использовать и атрибут REFERENCE. subroutine inarr(array) !dec$attributes stdcall, reference :: inarr integer(4), dimension(3, 2) :: array print *, 'Enter 6 integer values:' read(*, *) array array(2, 2) = -5 end subroutine inarr
! Текст функции запишем в файл ffun.f90 ! Исполняемый код получим командой ! DF cmain.obj ffun.f90 /nodformain /nodebug ! Введем 6*1 ! Этому элементу соответствует элемент ! array[1][1] в СИ-функции main
void main( ) { // Текст функции храним в файле cmain.c int array[2][3]; extern void __stdcall inarr(int array[][3]); inarr(array); printf("array[1][1] = %3d", array[1][1]); // array[1][1] = -5 }
Пример 2. Вызвать из Фортрана функцию СИ ввода массива. program fortmain interface subroutine cinarr(addr_array) !dec$attributes c :: cinarr integer(4) :: addr_array end subroutine cinarr end interface integer(4) :: array(3, 2) call cinarr(loc(array)) print *, 'array(2, 2) = ', array(2, 2) end program fortmain
! Храним код в файле fmain.f90 ! Интерфейс СИ-функции cinarr ! addr_array - адрес массива ! В СИ массив объявим, как array[2][3] ! Передаем адрес массива ! Исполняемый файл создаем командой ! DF fmain.f90 cfun.obj
void cinarr(int array[][3]) { // Код функции запишем в файл cfun.c int i, j; // Объектный файл cfun.obj получим командой puts("Enter 6 integer values:"); // CL /c cfun.c for(i = 0; i < 2; i++) // Введем 6 целочисленных значений for(j = 0; j < 3; j++) scanf("%d", &array[i][j]); array[1][1] = -5; // Этому элементу соответствует элемент } // array (2, 2) в программе fortmain
Замечание. Если в интерфейсе СИ-функции cinarr объявить не адрес массива, а сам массив: integer(4) :: array(3, 2) cinarr(array)
!
Заголовок
⎯ 240 ⎯
подпрограммы:
subroutine
7. Программирование на нескольких языках
то вызов СИ-функции cinarr выполняется так: call cinarr(array)
! Передаем адрес массива
Этот вызов, как и предыдущий, обеспечивает передачу массива по ссылке. В Бейсике передачу массива в подпрограмму Фортрана проиллюстрируем схемой: ' Код Бейсика Declare Sub FORTARRAY Lib "fortarr.dll" (Barray as Single) DIM barray (1 to 3, 1 to 7) As Single Call FORTARRAY(barray(1, 1)) ! Код Фортрана subroutine FORTARRAY(arr) real arr(3,7)
7.7.1.3. Передача ссылок и размещаемых массивов Фортрана Используя ссылки и размещаемые массивы Фортрана как параметры иноязычных процедур, можно передать либо базовые адреса объектов, либо их описатели. Способ передачи регулируется атрибутами DEC и приведен в табл. 7.9. Таблица 7.9. Влияние атрибутов на способ передачи ссылок и размещаемых массивов Атрибут объекта
Атрибуты передающей процедуры
Что передается
Нет
По умолчанию
Описатель
Нет
C или STDCALL
Базовый адрес
Нет
C, REFERENCE или STDCALL, REFERENCE
Описатель
VALUE
Без атрибута или C или STDCALL с атрибутом REFERENCE или без него
Базовый адрес
REFERENCE
То же
Описатель
Замечание. Атрибут VALUE может быть назначен только размещаемым массивам. Описатель является структурой, содержащей ссылку на адрес области, занимаемой объектом, и описания свойств ссылки или размещаемого массива, таких, как ранг, границы и др. Используемые с описателем структуры и программы приведены в прил. 2. ⎯ 241 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Пример. Выполнить изменение ссылочного массива в СИ-функции cpoint. program fortmain interface subroutine cpoint(addr_parr) ! Интерфейс СИ-функции cpoint !dec$attributes c :: cpoint integer(4) :: addr_parr ! Адрес ссылки end subroutine cpoint end interface integer(4), dimension(:, :), pointer :: parr => null( ) integer(4), target, dimension(3, 2) :: arr parr => arr ! Прикрепляем ссылку к адресату call cpoint(loc(parr)) ! Передаем адрес ссылки print '(10i3)', parr ! 0 1 2 1 -5 3 print *, arr ! 0 1 2 1 -5 3 end program fortmain void cpoint(int parr[][3]) { int i, j; for(i = 0; i < 2; i++) for(j = 0; j < 3; j++) parr[i][j] = i + j; parr[1][1] = -5; 2) }
// Код функции храним в файле cfun.c // Введем 6 целочисленных значений // 0 1 2 1 2 3 // Имя этого элемента в Фортране - parr (2,
7.7.1.4. Передача целочисленных указателей Целочисленные указатели Фортрана подобны указателям СИ и занимают в памяти 4 байта. Передавая целочисленные указатели из Фортрана в процедуру, написанную на другом языке, придерживайтесь правил:
•
передаваемый из Фортрана параметр должен быть целочисленным указателем, а не адресной переменной, с которой этот указатель связан;
•
в нефортрановской процедуре параметр должен быть объявлен как указатель, тип которого соответствует типу адресной переменной Фортрана. Пример:
program fortmain interface subroutine cfun(pa) !dec$attributes c :: cfun integer(4) :: pa
! Код запишем в файле fmain.f90 ! Исполняемый файл создаем командой ! DF fmain.f90 cfun.obj /nodebug
⎯ 242 ⎯
7. Программирование на нескольких языках
end subroutine cfun end interface real(4), dimension(10) :: array, var pointer(pa, var) pa = loc(array) call cfun(pa) print *, ' array(5) = ', array(5) end program fortmain
! var - адресная переменная ! Тип pa - INTEGER(4)
void cfun(float *pa) { pa[4] = 55.5; }
// Код СИ-функции храним в файле cfun.c // Изменение pa повлечет изменение array // Компилируем командой CL /c cfun.c
! array(5) =
55.50000
Принимая в Фортране указатель из процедуры, написанной на другом языке, выполняйте условия: •
параметр, получаемый Фортраном, следует объявить как целочисленный указатель. Оператор POINTER должен ассоциировать его с адресной переменной, тип которой соответствует типу передаваемого указателя;
•
в нефортрановской процедуре параметр должен быть объявлен как указатель соответствующего типа и передан привычным образом. Пример:
subroutine fpoint(pa) !dec$attributes c :: fpoint real(4) :: var(10) pointer(pa, var) var(5) = 55.5 end subroutine fpoint
! Код запишем в файле ffun.f90 ! Исполняемый файл создаем командой ! DF cmain.obj ffun.f90 /nodformain /nodebug
void main( ) { float array[10]; extern void fpoint(float *); массива array fpoint(array); printf("array[4] = %6.2f", array[4]); }
// Код СИ-функции храним в файле cmain.c //
Тип
указателя
определяется
// array[4] = 55.50 // Компилируем командой CL /c cmain.c
7.7.1.5. Имена модулей
Рассмотрим модуль module fortmod integer(4) :: ia contains subroutine modsub(b)
типом
! Разместим модуль в файле fmod.f90 ! Модульная подпрограмма
⎯ 243 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
real(4) :: b b = 3.0 end subroutine modsub end module fortmod
Выполним компиляцию модуля командой DF fmod.f90 /c /nodebug. Тогда в объектном файле fmod.obj компилятор преобразует определенные в модуле fortmod имена ia и modsub следующим образом: •
имя ia - в _FORTMOD_mp_IA
•
имя modsub - в _FORTMOD_mp_MODSUB@4 Общее правило преобразования имен таково: имя entity модуля MODULEMANE преобразовывается в имя _MODULEMANE_mp_ENTITY[@размер стека] Причем независимо от применяемого соглашения составляющие имени MODULEMANE и ENTITY всегда записываются прописными буквами. Составляющая имени @размер стека присутствует только в имени модульной процедуры в случаях, когда не задано соглашение C. Если же оно задано, то составляющая @размер стека компилятором не генерируется, например: module fortmod integer(4) :: ia contains subroutine modsub(b) !dec$attributes с :: modsub объектном ...
! Разместим модуль в файле fmod.f90
!
Имя
модульной
подпрограммы
в
! файле - _FORTMOD_mp_MODSUB
7.7.1.6. Доступ к объектам модулей Фортрана в функциях СИ Рассмотрим модуль fortmod, ссылка на который выполняется в программе fortmain и объекты которого используются СИ-функцией cfun. module fortmod integer(4) :: ia real(4), dimension(5) :: arr character(80) :: string = ' ' строки type modtype logical(4) :: flag character(20) :: name end type modtype
! Разместим модуль fortmod и главную ! программу fortmain в файле fmain.f90 ! Желательно выполнить инициализацию ! иначе она будет заполнена null-символами
⎯ 244 ⎯
7. Программирование на нескольких языках
type(modtype) :: mtp = modtype(.false., ' ') contains subroutine modsub(b) !dec$attributes c, reference :: modsub ! Параметр b передадим по ссылке real(4) :: b b = 3.0 end subroutine modsub end module fortmod program fortmain use fortmod interface subroutine cfun( ) !dec$attributes c :: cfun end subroutine cfun end interface call cfun( ) print *, ia, arr(3), ' ', trim(string) print *, mtp.flag, ' ', trim(mtp%name) end program fortmain
! Интерфейс СИ-функции cfun
! Изменим объявленные в модуле данные ! Данные модифицировались в СИ-функции ! cfun
Доступ к объектам модуля в СИ выполняется непосредственно: объекты модуля объявляются как внешние (внутри или вне функций) и затем они используются в функциях СИ, например: #include <string.h> // Ведущий символ подчеркивания перед внешним extern int FORTMOD_mp_IA; // именем в исходном коде должен отсутствовать extern float FORTMOD_mp_ARR[5]; extern char FORTMOD_mp_STRING[80]; extern struct { int flag; char name[20]; } FORTMOD_mp_MTP; extern void FORTMOD_mp_MODSUB(float *b); void cfun( ) { float b; FORTMOD_mp_IA = -3; // Модифицируем данные модуля fortmod FORTMOD_mp_ARR[2] = 55.0; // Меняем значение третьего элемента массива strcpy(FORTMOD_mp_STRING, "Entered in C function"); FORTMOD_mp_MTP.flag = 1; strcpy(FORTMOD_mp_MTP.name, "Mike & Steve"); FORTMOD_mp_MODSUB(&b);
⎯ 245 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
printf("\nb = %7.3f\n", b); }
Замечание. При работе со строками, полученными из СИ, следует помнить, что они завершаются символом конца строки - null-символом. 7.7.1.7. Определение модульной процедуры в СИ Если модульная процедура написана на другом языке программирования, например на СИ, то для ее использования в Фортране следует в модуле, которому эта процедура принадлежит, объявить ее интерфейс. Пусть задана СИ-функция cname. #include <math.h> float cname(float x, float y) { С return sqrt(x * x + y * y); }
// Для встроенной функции sqrt // По умолчанию используется соглашение // Код разместим в файле cfun.c
которую надо использовать в качестве модульной процедуры модуля fortmod. Чтобы получить такую возможность, достаточно объявить в модуле интерфейс этой функции: module fortmod interface function cname(x, y) !dec$attributes c :: cname real(4) :: cname, x, y end function cname end interface end module fortmod program fortmain use fortmod print *, cname(3.0, 4.0) end program fortmain
! Разместим модуль fortmod и главную ! программу fortmain в файле fmaim.f90 ! Исполняемый файл получим командой ! DF fmain.f90 cfun.obj /nodebug
! Вызов модульной функции cname
7.7.2. Использование common-блоков Фортрана и структур СИ 7.7.2.1. Прямой доступ к common-блокам Фортрана и структурам СИ Данные, объединяемые в Фортране common-блоком, в СИ представляются в виде структуры. Однако при построении структуры СИ, соответствующей common-блоку Фортрана, следует учитывать, как выравнены данные в Фортране.
⎯ 246 ⎯
7. Программирование на нескольких языках
По умолчанию данные Фортрана выравниваются так: •
переменные common-блока типа BYTE, INTEGER(1), LOGICAL(1) и CHARACTER размещаются немедленно вслед за последним занятым байтом памяти;
•
переменные других типов начинаются на следующем четном байте, ближайшем к последнему байту, занятому переменными common-блока; то же справедливо и для массивов всех, кроме CHARACTER, типов;
•
массивы типа CHARACTER начинаются немедленно на первом свободном байте;
•
сами common-блоки начинаются на адресе, кратном числу 4.
Если переменные common-блока размещены в памяти по приведенным правилам (а это достигается, когда при компиляции Фортран-файла отсутствует опция /align:commons или /align:dcommons), то необходимо изменить правила размещения данных в соответствующей СИ-структуре, применив директиву #pragma pack(2). Пусть, например, задан common-блок a1: program fortmain fmain.f90 !dec$attributes с :: a1 вызовах, logical(2) :: flag имя integer(4), dimension(3) :: iarray character(len = 7) string1 common/a1/ flag, iarray, string1 interface subroutine cfun( ) !dec$attributes c :: cfun end subroutine cfun end interface call cfun( ) print *, flag, iarray(3), ' ', string1(1:1) end program fortmain
! Программу fortmain разместим в файле !
Обязательное
задание
соглашения
о
! определяющего в данном случае внешнее ! common-блока a1 как _a1
!T
55 R
Построение EXE-файла выполним командой DL fmain.f90 cfun.obj /nodebug, в которой cfun.obj - объектный файл СИ-функции cfun, использующей данные common-блока a1 и размещенной в файле cfun.c. Тогда common-блоку a1 соответствует приведенная в функции cfun СИструктура a1: ⎯ 247 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
void cfun( ) { #pragma pack(2) (компонентов и extern struct { short int flag; int iarray[3]; char string1[7]; } a1; #pragma pack( ) a1.flag = 1; a1.iarray[2] = 55; a1.string1[0] = 'R'; }
//
Выравнивание
СИ-структуры
// short int и int) по 2-байтовой границе
// Внешнее имя структуры - _a1 // Восстанавливаем заданное по умолчанию // выравнивание
Если же при построении исполняемого файла добавить опцию /align:commons, то директивы #pragma pack должны быть опущены: void cfun( ) { extern struct { // Естественное выравнивание компонентов short int flag; // структуры a1 int iarray[3]; char string1[7]; } a1; // Внешнее имя структуры - _a1 a1.flag = 1; a1.iarray[2] = 55; a1.string1[0] = 'R'; }
Замечание. Атрибут, задающий внешнее имя common-блока a1, !dec$attributes с :: a1
! Внешнее имя common-блока a1 - _a1
можно заменить выполняющим те же функции атрибутом ALIAS: !dec$attributes alias : '_a1' :: a1
! _a1 - внешнее имя common-блока a1
7.7.2.2. Передача адреса common-блока Чтобы передать адрес common-блока, нужно передать адрес его первой переменной. При этом СИ-функция, принимающая этот адрес, должна в качестве параметра принять адрес структуры, соответствующей commonблоку Фортрана. Например: program fortmain fmain.f90 integer(4) :: ival character(len = 7) string1 common/b2/ ival, string1 interface subroutine cfun(block) !dec$attributes c :: cfun integer(4) :: block
! Программу fortmain разместим в файле ! Исполняемый файл получим командой ! DF fmain.f90 cfun.obj /nodebug
! block -адрес common-блока b2
⎯ 248 ⎯
7. Программирование на нескольких языках
end subroutine cfun end interface call cfun(loc(ival)) блока b2 print *, ival, ' ', string1(1:1) end program fortmain extern struct b2 { int ival; используем char string1[7]; } block;
! Передаем адрес первого элемента common!
22 R
// СИ-код храним в файле cfun.c // Для получения объектного
кода
// команду CL /c cfun.c // Внешнее имя структуры - _b2
void cfun(struct b2 *block) { block->ival = 55; block->string1[0] = 'R'; }
Адрес b2 будет также передан в СИ-функцию cfun, если в нее передать первый элемент common-блока по ссылке. Для этого в Фортран-интерфейсе СИ-функции cfun используем атрибут REFERENCE: ... interface subroutine cfun(block) !dec$attributes c :: cfun !dec$attributes reference :: block integer(4) :: block end subroutine cfun end interface call cfun(ival) ...
! Соответствующий фактический параметр ! передается по ссылке ! Параметр ival передается по ссылке
Замечания: 1. Используйте в команде DF опции /align:commons или /align:dcommons, которые обеспечивают естественное выравнивание данных commonблоков. Следствием этого является не только согласованность данных со структурами СИ, но и повышение быстродействия приложения. 2. Если первым элементом common-блока является массив, то применение для него атрибута REFERENCE будет ошибкой, поскольку массив всегда передается по ссылке.
7.7.3. Передача производных типов данных Производные типы данных Фортрана подобны структурам СИ. Переменные этих типов могут быть переданы через модули Фортрана и common-блоки. Разумеется, процедурам на другом языке, в которые ⎯ 249 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
передаются объекты производных типов, должны быть известны определения этих типов. Пример. Изменить переменную tp типа newtype в СИ-функции change_tp. program fortmain ! Код запишем в файле fmain.f90 interface ! Исполняемый файл создаем командой subroutine change_tp( ) ! DF fmain.f90 cfun.obj /nodebug !dec$attributes c :: change_tp end subroutine change_tp end interface type newtype sequence ! Обязательный при размещении данных integer(4) :: array(10) ! в common-блоке атрибут SEQUENCE complex(4) :: z character(30) :: string end type newtype type(newtype) :: tp = newtype(1, (1.0, 1.0), ' ') !dec$attributes alias : '_a_block' :: a_block common/a_block/ tp call change_tp( ) print *, tp%array(5), tp%z, ' ', tp%string(1:10) ! Результат: 55 (3.000000, 4.000000) New value end program fortmain #include <string.h> extern struct { struct { int array[10]; struct{ float real, imag; } z; char string[30]; } tp; } a_block;
// СИ-код храним в файле cfun.c // и компилируем командой CL /c cfun.c // Комплексный тип данных
void change_tp( ) { a_block.tp.array[4] = 55; // Изменяем данные структуры a_block.tp.z.real = 3.0; a_block.tp.z.imag = 4.0; strcpy(a_block.tp.string, "New value"); }
⎯ 250 ⎯
7. Программирование на нескольких языках
7.8. Особенности одновременного использования Фортрана и СИ Приложение, использующее Фортран и СИ, может быть создано командами CL и DF, запускаемыми из командной строки, либо в среде Visual Studio DVF. В последнем случае в проект Фортрана следует включить объектные файлы СИ либо указать пути к библиотекам, содержащим эти файлы. При выводе данных из процедур, написанных как на Фортране, так и на СИ, если не принять специальных мер, результаты могут появляться не в том порядке, какой предусмотрен алгоритмом. Это объясняется тем, что СИ накапливает данные в буфере и выполняет вывод после того, как буфер заполнился целиком. Чтобы восстановить требуемый порядок вывода, следует принудительно освобождать СИ-буфер, применяя функции flushall, fflush, fclose, setbuf или setvbuf. При многониточном программировании должны быть использованы соответствующие библиотеки и в Фортране и в СИ: DFORMT.LIB - для Фортрана и LIBCMT.LIB - для СИ.
7.9. Включение Фортран-процедур в приложения на Бейсике При совмещении Фортрана и Бейсика (32-битовый Microsoft Visual Basic 4-й версии) используются вычислительные возможности Фортрана и средства Бейсика по построению интерфейсов. Включение файлов Бейсика в Фортран невозможно, поскольку Бейсик не компилируется, а интерпретируется без создания объектного кода. Вместо этого Бейсик создает OLE-объекты, в которые экспортируются свойства и процедуры и которые можно использовать для вызова Бейсика из Фортрана. Создадим простое приложение, содержащее кнопку, активизирующую код Бейсика, в котором выполняется вызов процедуры Фортрана. Откроем Visual Basic и сделаем следующее: 1) выполним File - New project. Добавим из панели инструментов в форму командную кнопку и текстовое поле. Изменим название (Caption) командной кнопки, ударив по ней мышью и выбрав в появившемся списке свойств поле Caption; 2) ударим дважды мышью по командной кнопке и в появившееся окно занесем код: ⎯ 251 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Private Sub Command1_Click( ) Static arr(1 To 5) As StringArray Call FORSTRARR(arr(1)) Text1.Text = "" For i% = 1 To 5 Text1.Text = arr(i%).strings + Chr$(13) + Chr$(10) Next i% End Sub
3) выполним File - Save project As и дадим форме имя vbfort.frm; проект сохраним под именем vbfort.mak; 4) выполним File - New Module и добавим в появившееся окно модуля код: Type StringArray strings As String * 35 End Type Declare Sub FORSTRARR Lib "d:\programs\vb4\fortvb.dll" (Myarray As StringArray)
5) сохраним модуль под именем vbfort.bas. Подпрограмму FORSTRARR Фортрана оформим в виде DLL-файла. Создадим для этого в среде VS DVF проект DLL и добавим в него файл под именем fortvb.f90 с кодом: subroutine forstrarr(arr) character(35) arr(5) arr = "This is a string from Fortran." end subroutine
Выполним построение файла fortvb.dll и скопируем его в директорию d:\programs\vb4. Выберем пункт меню Run и запустим проект на исполнение.
7.10. Создание приложений на Фортране и Ассемблере Ассемблер (Microsoft Macro Assembler) работает с Фортраном и СИ/СИ++. Порядок соединения Фортрана и Ассемблера такой же, как и в случае Фортрана и СИ: прежде получается объектный код программы на Ассемблере, затем командой DF создается приложение, в котором используются и код Фортрана и код Ассемблера.
⎯ 252 ⎯
7. Программирование на нескольких языках
7.10.1. Формирование результата функцией Ассемблера Процедура Ассемблера может возвращать значение, если ее прототип описан как функция. Данные размеров в 4 байта или меньше, кроме чисел с плавающей точкой, возвращаются в регистр EAX. Процедуры, возвращающие значение с плавающей точкой, помещают результат в стек процессора с плавающей точкой. Чтобы вернуть из Ассемблера в Фортран значения, большие 4 байтов, например комплексные числа и символьные строки, необходимо использовать особые соглашения. Функции Фортрана, возвращающие подобные величины, работают так: функция создает область в стеке для хранения реально возвращаемого значения. Когда вызывается процедура Ассемблера, в нее передается дополнительный параметр - адрес созданного пространства. Этот параметр передается последним. Процедура Ассемблера размещает результат по адресу, переданному дополнительным параметром, а затем копирует результат в регистр EAX. Возможные способы размещения результата приведены в табл. 7.10. Таблица 7.10. Размещение результата функцией Ассемблера Тип возвращаемого результата
Куда направляется результат
Целая или логическая величина размером в 4 байта или меньше
В регистр EAX
Переменная с плавающей точкой
В стек процессора с плавающей точкой
Структуры, размер которых превышает 4 байта
Возвращаемый результат - в стек; адрес результата - в регистр EAX
В случае структур следует обращать внимание на выравнивание данных, которое выполняется в Ассемблере и Фортране, по-разному: по умолчанию Ассемблер выравнивает структуры по 1-байтовой границе, а Фортран - по 8-байтовой. Это относится и ко вложенным структурам. Выход из положения - задать в Ассемблере выравнивание структур по слову.
7.10.2. Примеры программ на Фортране и Ассемблере В приводимых примерах рассмотрены варианты передачи различных типов данных из Фортрана в Ассемблер и обратно, а также варианты использования в Фортране процедуры Ассемблера и в качестве функции, и
⎯ 253 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
в качестве подпрограммы. Темы примеров взяты из поставляемой с Фортраном документации. В каждом примере получение объектного кода процедуры Ассемблера выполняется командой ML /c asmfile.asm генерирующей файл asmfile.obj. Используемая версия Ассемблера - MASM 6.0 фирмы Microsoft. Исполняемый файл fmain.exe получается командой DF fmain.f90 asmfile.obj /nodebug в которой fmain.f90 - имя файла, содержащего главную программу Фортрана, выполняющую вызов процедуры Ассемблера. Пример 1. Вызвать из Фортрана процедуру Ассемблера, которая вычисляет значение выражения a * 2 ** b. ! Текст главной Фортран-программы, вызывающей процедуру Ассемблера program fortmain ! Код храним в файле fmain.f90 interface function power2(a, b) ! _POWER2 - внешнее имя функции power2 !dec$attributes alias : '_POWER2' :: power2 integer(2) :: power2, a, b end function power2 end interface integer(2) :: a, b a = 3; b = 5 print *, '3 * 2 ** 5 = ', power2(a, b) end program fortmain ; Код на Ассемблере, вычисляющий выражение value * 2 ** exponent .386 .MODEL FLAT, STDCALL POWER2 PROTO STDCALL, value:PTR SDWORD, exponent:PTR SBYTE .CODE POWER2 PROC STDCALL, value:PTR SDWORD, exponent:PTR SBYTE mov ecx, value ; Загружаем адрес множителя value mov eax, [ecx] ; Загружаем сам множитель mov ecx, exponent ; Загружаем адрес показателя степени mov cl, [ecx] ; Загружаем показатель степени shl eax, cl ; Вычисляем 2 ** exponent ret ; Оставляем результат в регистре EAX POWER2 ENDP END
⎯ 254 ⎯
7. Программирование на нескольких языках
Пример 2. Вызвать из Фортрана процедуру Ассемблера, которая преобразовывает английские буквы строки Фортрана в прописные. ! Текст главной Фортран-программы, вызывающей процедуру Ассемблера, ! преобразующей строчные буквы в прописные program fortmain ! Код храним в файле fmain.f90 interface ! Используем заданное по умолчанию соглашение subroutine upper(string) ! Внешнее имя подпрограммы - _UPPER !dec$attributes alias : '_UPPER' :: upper character(*) string end subroutine upper end interface character(20) :: string = 'This is some text' print *, 'Mixed case: ', string ! Mixed case: This is some text ! Вместе со строкой передается и второй, скрытый, параметр - длина строки call upper(string) print *, 'Upper case: ', string ! Upper case: THIS IS SOME TEXT end program fortmain ; Код на Ассемблере, переводящий строчные английские буквы ; строки Фортрана в прописные .386 .MODEL FLAT, C ; Строка благодаря обозначению PTR передается по ссылке ; Вместе со строкой передается и второй, скрытый, параметр - длина строки ; text и ltext - строка, подлежащая преобразованию, и ее длина UPPER PROTO C, text:PTR SBYTE, ltext:DWORD .CODE UPPER PROC C, text:PTR SBYTE, ltext:DWORD mov edx, text ; Загружаем адрес начала строки mov ecx, edx ; Копируем стартовый адрес add ecx, ltext ; Вычисляем адрес конца строки jmp first ; Переход на начало цикла next: mov al, [edx] ; Загружаем следующий символ inc edx ; Увеличиваем адрес символа cmp al, 'a' ; Сравниваем с первой строчной буквой ; Переход к следующему символу, если он меньше 'a', иначе - продолжить jb first cmp al, 'z' ; Сравниваем с последней строчной буквой ; Переход к следующему символу, если он больше 'z', иначе - продолжить ja first sub al, 32 ; Преобразовываем в прописную букву
⎯ 255 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
mov [edx-1], al first: cmp edx, ecx jbe next ret UPPER ENDP END
; Помещаем символ обратно в строку ; Проверяем, достигнут ли конец строки ; Продолжаем, если это не так ; Выход при достижении конца строки
Пример 3. Вызвать из Фортрана функцию Ассемблера, возвращающую строку, в которой строчные английские буквы преобразованы в прописные. В этой задаче выполняются те же преобразования, что и в предыдущей: отличие в том, что процедура Ассемблера UPPER2 вызывается в Фортране как функция, а не как подпрограмма. ! Текст главной Фортран-программы, вызывающей функцию Ассемблера, ! преобразующей строчные буквы в прописные program fortmain ! Код храним в файле fmain.f90 interface ! Используем заданное по умолчанию соглашение function upper2(string) ! Внешнее имя подпрограммы - _UPPER2 !dec$attributes alias : '_UPPER2' :: upper2 character(25) :: upper2 character(*) :: string end function upper2 end interface character(20) :: string1 = 'This is some text.' character(30) :: string2 = 'This is some more longer text.' print *, 'UPPER1 = ', upper2(string1) ! UPPER1 = THIS IS SOME TEXT. print *, 'UPPER2 = ', upper2(string2) ! UPPER2 = THIS IS SOME MORE LONGER end program fortmain ; Функция UPPER2 возвращает строку, в которой строчные буквы преобразованы ; в прописные .386 .MODEL FLAT, C ; Строка благодаря обозначению PTR передается по ссылке ; Вместе со строкой передается и второй, скрытый, параметр - длина строки ; retval и lretval - скрытый адрес результирующей строки и ее длина ; text и ltext - строка, подлежащая преобразованию, и ее длина ; В Фортране функция типа CHARACTER(*) передает адрес, по которому ; размещается результат, и длину результата UPPER2 PROTO C, retval:PTR SBYTE, lretval:DWORD, text:PTR SBYTE, ltext:DWORD .CODE
⎯ 256 ⎯
7. Программирование на нескольких языках
UPPER2 PROC C USES edi esi, retval:PTR SBYTE, lretval:DWORD, text:PTR SBYTE, ltext:DWORD mov esi, text ; Загружаем адреса обеих строк mov edi, retval mov edx, ltext ; Загружаем длину обеих строк mov ecx, lretval cmp ecx, edx ; Сравниваем длину строк jbe next ; Усекаем строку text, если больше результата, xchg ecx, edx ; иначе добавляем хвостовые пробелы sub edx, ecx ; Вычисляем число добавляемых пробелов next: lodsb ; Загружаем следующий символ cmp al, 'a' ; Сравниваем с первой строчной буквой ; Переход к следующему символу, если он меньше 'a', иначе - продолжить jb gotcase cmp al, 'z' ; Сравниваем с последней строчной буквой ; Переход к следующему символу, если он больше 'z', иначе - продолжить ja gotcase sub al, 32 ; Преобразовываем в прописную букву gotcase: stosb ; Размещаем символ в строку - результат loop next ; Продолжаем, пока не скопированы все символы cmp edx, ltext ; Если EDX не изменен, готово je done mov al, ' ' ; Загружаем пробел mov ecx, edx ; Копируем число оставшихся пробелов rep stosb ; Сохраняем их по адресу результата done: mov eax, retval ; Загружаем адрес результата ret ; Выход. Адрес результата размещен в EAX UPPER2 ENDP END
Пример 4. Вызвать из Фортрана функции Ассемблера, возвращающие вещественную и комплексную величину. program fortmain ! Код храним в файле fmain.f90 interface function returnr( ) !dec$attributes alias : '_RETURNR' :: returnr real(4) :: returnr
⎯ 257 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
end function returnr function returnc( ) !dec$attributes alias : '_RETURNC' :: returnc complex(4) :: returnc end function returnc end interface print *, 'Real = ', returnr( ) ! Real = 5.000000 print *, 'Complex = ', returnc( ) ! Complex = (1.000000, 2.000000) end program fortmain ; Процедура в зависимости от использованного при вызове имени возвращает ; либо вещественное, типа REAL4, либо комплексное, типа COMPLEX8, число .386 .MODEL FLAT, STDCALL COMPLEX8 STRUCT 4 real REAL4 ? imag REAL4 ? COMPLEX8 ENDS RETURNR PROTO STDCALL RETURNC PROTO STDCALL, retval: PTR COMPLEX8 .DATA ; Объявление формирующих результат данных r REAL45.0 cr REAL4 1.0 ci REAL4 2.0 .CODE RETURNR PROC STDCALL fld r ret RETURNR ENDP
; Загружаем результат типа REAL4 ; Результат находится в стеке процессора
RETURNC PROC STDCALL, retval: PTR COMPLEX8 mov edx, retval ; Загружаем адрес возвращаемой структуры fld cr ; Загружаем вещественный компонент fst (COMPLEX8 PTR [edx]).real ; Сохраняем вещественный компонент fld ci ; Загружаем мнимый компонент fst (COMPLEX8 PTR [edx]).imag ; Сохраняем мнимый компонент mov eax, edx ; Перемещаем адрес возвращаемой структуры в EAX ret ; Возвращаем адрес результата в регистре EAX RETURNC ENDP END
⎯ 258 ⎯
Приложение 1. Директивы DVF П. 1.1. Обзор директив Директивы - это специальные включаемые в программу инструкции, которые сообщают компилятору, какие следует предпринять действия при компиляции программы. Помимо директив процесс компиляции может управляться и из командной строки (см. гл. 6). Однако управление директивами является более гибким, поскольку последние могут быть включены, затем выключены или изменены в различных местах исходного текста программы. При возникновении конфликта между опцией компилятора и директивой приоритет имеет директива. Воздействие директивы распространяется на часть кода, расположенного после появления в нем директивы. В большинстве случаев действие директивы можно отменить или изменить. Так, многие директивы имеют директиву, отменяющую их действие, например $DECLARE и $NODECLARE, или могут быть заданы с различными значениями, например $REAL:8 устанавливает задаваемый по умолчанию размер вещественного типа данных равным 8 байтам, а $REAL:4 устанавливает этот размер равным 4 байтам. Таким образом, одна часть программы может быть откомпилирована согласно директиве $REAL:8, а другая - $REAL:4; в одной части программы код может быть записан в фиксированном (директива $NOFREEFORM), а в другой - в свободном ($FREEFORM) формате. И т. д. Директивы подразделяются на категории: •
директивы $STRICT, $NOFREEFORM и $FIXEDFORMLINESIZE, сообщающие компилятору о необходимости выявления в исходном коде отличий от стандарта ($STRICT) или заданного формата. Используются, если необходимо создать переносимый на другие компиляторы код. Для первых двух директив существуют обратные: $NOSTRICT и $FREEFORM;
•
директивы, обеспечивающие компиляцию фрагмента программы при выполнении некоторого условия: $DEFINE, $UNDEFINE, $IF, $IF DEFINED, $ELSE, $ELSEIF и $ENDIF;
•
директивы, управляющие $NODECLARE и $MESSAGE;
процессом
⎯ 259 ⎯
отладки:
$DECLARE,
О. В. Бартеньев. Visual Fortran: новые возможности
•
директивы, изменяющие задаваемый по умолчанию размер встроенных целого и вещественного типов данных: $INTEGER и $REAL;
•
директивы, управляющие печатью листинга исходного кода: $TITLE и $SUBTITLE;
•
директива $OBJCOMMENT, помещающая полное имя библиотеки в объектный код для ее поиска компоновщиком;
•
директива $OPTIONS управляет упаковкой данных производных типов и общих блоков;
•
директива $PACK, управляющая начальным адресом сохранения компонентов производных типов;
•
директива $PSECT изменяет некоторые характеристики общих блоков;
•
директива $ATTRIBUTES объявляет DEC-атрибуты;
•
директива $ALIAS задает альтернативное внешнее имя процедуры или общего блока;
файла
•
директива $IDENT задает идентификатор объектного файла. Замечание. Необязательные элементы директив будут заключаться в квадратные скобки.
П. 1.2. Использование директив Директиве компилятора предшествует префикс [cDEC]$ в котором c - это C (или c) или !; первые 4 символа префикса - cDEC - могут быть опущены. Любая строка исходного текста, содержащая символ доллара ($) в первой позиции или начинающаяся с символов cDEC$, интерпретируется как директива. С префиксом связаны правила: 1) префикс, начинающийся с C (или c), допустим лишь в коде, написанном в фиксированном формате, причем он должен появляться в позициях 15; 2) префикс, начинающийся с !, допустим при любом формате исходного кода, однако при фиксированном формате размещение префикса подчиняется правилам, приведенным в пп. 1 и 3. Символы !DEC$ могут начинаться в любой позиции строки. Им могут предшествовать только пробелы и символы табуляции; ⎯ 260 ⎯
Приложение 1. Директивы DVF
3) буквы DEC могут быть прописными, строчными или смешанными; 4) четыре символа сDEC$ должны быть записаны без встроенных пробелов; пробелы после $ игнорируются; 5) любая директива может начинаться с префикса !MS$. Примеры: !dec$strict $strict !dec$ freeform
! Пробелы между !DEC$ и FREEFORM допустимы
После директивы можно добавить комментарий, который должен предваряться восклицательным знаком, например: !dec$freeform
! Свободный формат записи исходного кода
Замечание. В случае ошибочного задания директивы она, если в качестве префикса используется CDEC$ или !DEC$, воспринимается как комментарий и ожидаемого эффекта не достигается. Поэтому надо предельно внимательно отслеживать синтаксис используемых директив. Директива и ее параметры должны располагаться на одной строке программы, поскольку продолжение строки с директивой невозможно. Большинство директив могут размещаться на любой строке исходного файла. Их действие, если они не отменены другой директивой, распространяется до конца файла. Это позволяет задавать разные директивы компилятору для разных фрагментов исходного кода. Однако существуют ограничения. Нельзя разместить директиву $OPTIONS среди исполняемых операторов. Директивы $INTEGER, $REAL, $STRICT и $NOSTRICT могут появляться только в верхней части программной единицы: в головной программе, во внешней процедуре, в модуле и BLOCK DATA. Действие директив распространяется на любой включаемый (INCLUDE) файл, который, правда, может содержать свои собственные директивы. Директивы, расположенные внутри включаемого файла, оказывают действие как на код включаемого файла, так и на расположенный вслед за ним код файла-хозяина, т. е. файла, в который выполняется вставка кода. Однако если директива внутри включаемого файла изменяет формат записи исходного кода или длину строки ($FREEFORM, $NOFREEFORM или $FIXEDFORMLINESIZE), то ее действие распространяется только на включаемый файл и не затрагивает код файла-хозяина.
⎯ 261 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Директивы программы, которая подключает модуль оператором USE, не оказывают влияния на код модуля. Поэтому для управления компиляцией модуля в него следует включить свои собственные директивы. Поскольку директива $PACK оказывает воздействие на распределение памяти, то ее нельзя размещать внутри управляющих конструкций (например, внутри конструкции IF - THEN - ELSE) или внутри определений производных типов данных. Конструкция с директивой $IF может включать обычные операторы Фортрана. Правда, эти операторы должны быть исключены при использовании кода в других системах программирования.
П. 1.3. Директивы, контролирующие правила написания исходного кода П. 1.3.1. Директивы $STRICT и $NOSTRICT Директива $STRICT указывает компилятору на необходимость выявления элементов DVF, не соответствующих стандарту Фортрана. $NOSTRICT (задается по умолчанию) позволяет использование всех имеющихся в DVF расширений. Синтаксис директив: cDEC$STRICT и cDEC$NOSTRICT Директивы $STRICT и $NOSTRICT могут появляться только в верхней части программной единицы: главной программе, внешней процедуре, модуле или BLOCK DATA - и не могут появляться между программными единицами или в начале внутренней процедуры. Они не оказывают влияния на включаемые оператором USE модули. Пример: type stuff integer(4) :: k, m character(40) :: name end type stuff type (stuff) :: examp double complex :: cd=(3.0d0,4.0d0) examp.k = 4 end
! $NOSTRICT по умолчанию
! Нестандартный тип - нет ошибки ! Нестандартный селектор ! компонента - нет ошибки
subroutine strictdemo( ) !dec$strict type stuff
⎯ 262 ⎯
Приложение 1. Директивы DVF
integer(4) :: k, m character(40) :: name end type stuff type (stuff) :: samp double complex :: cd = (3.0d0,4.0d0) samp.k = 4 end subroutine
! Ошибка. Правильно COMPLEX(8) ! Ошибка. Правильно samp%k = 4
П. 1.3.2. Директивы $FREEFORM и $NOFREEFORM Директива $FREEFORM позволяет записывать исходный код в свободном формате. Директива $NOFREEFORM, наоборот, указывает на необходимость записи текста программы в фиксированном формате (см. прил. 4). Синтаксис: cDEC$FREEFORM и cDEC$NOFREEFORM Существует 2 способа установить по умолчанию свободный формат записи исходного кода: •
использовать расширение F90 для файлов с исходным кодом;
•
использовать опцию компилятора.
Если ни один из этих способов не использован, то по умолчанию устанавливается фиксированный формат записи исходного кода. Дополнительно к существующим правилам умолчания можно установить директивой $FREEFORM свободный, а директивой $NOFREEFORM фиксированный формат записи исходного кода. Когда применена директива $FREEFORM или $NOFREEFORM, она действует на всю расположенную вслед за ней часть файла или до тех пор, пока не использована отменяющая ее директива. В случае применения директива воздействует на INCLUDE-файлы, но не действует на USEмодули, которые компилируются отдельно. Длину строки фиксированного формата можно изменить директивой $FIXEDFORMLINESIZE. Пример: !dec$freeform integer(4) :: k, m k = 4; m = 5 print 1, k, m 1 format(2i4) call demo( ) end
! Свободный формат записи исходного кода
subroutine demo( )
⎯ 263 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
$nofreeform integer(4) :: k = 4, m = 5 print 1, k, m 1 format(2i4) end subroutine demo
! Фиксированный формат. Для отображения ! в среде DVF разделяющих полос ! директиву следует начать с первой ! позиции строки и опустить !DEC
П. 1.3.3. Директива $FIXEDFORMLINESIZE Директива $FIXEDFORMLINESIZE устанавливает длину строки фиксированного формата исходного кода. Ее синтаксис: cDEC$FIXEDFORMLINESIZE: 72 | 80 | 132 Применяя директиву, можно установить длину строки в фиксированном формате равной 72, 80 или 132 символам. По умолчанию длина строки составляет 72 символа. $FIXEDFORMLINESIZE действует либо до конца файла, либо до следующей, изменяющей ее директивы. Как и другие директивы, она воздействует на INCLUDE-файлы, но не действует на USEмодули. Если INCLUDE-файл изменяет длину строки, то изменения не влияют на длину строки файла-хозяина. Директива $FIXEDFORMLINESIZE не оказывает действия на код, написанный в свободном формате. Пример: !dec$nofreeform !dec$fixedformlinesize:132 print *, 'Пишем эту строку за пределами 72-й колонки без продолжения'
П. 1.4. Условная компиляция программы П. 1.4.1. Директивы $DEFINE и $UNDEFINE Директива $DEFINE создает символическую переменную, существование которой может быть проверено во время условной компиляции. $UNDEFINE удаляет созданную $DEFINE переменную. Синтаксис: cDEC$DEFINE symbol-name [ = val] и cDEC$UNDEFINE symbol-name symbol-name - имя переменной, содержащее до 31 символа; может начинаться с $ и знака подчеркивания и не может начинаться с цифры. val - присвоенное symbol-name значение. Тип val - INTEGER(4). Директивы $DEFINE и $UNDEFINE создают и удаляют переменные для использования с директивами $IF и $IF DEFINED. Имя, определенное директивой $DEFINE, является локальным (используется только другими ⎯ 264 ⎯
Приложение 1. Директивы DVF
директивами и недоступно в программе) и поэтому может быть повторно объявлено в Фортран-программе. То есть $DEFINE-имена могут дублировать имена объектов данных программы без каких-либо конфликтов. Для проверки того, определен символ или нет, используется директива $IF DEFINED (symbol-name). Для проверки присвоенного symbol-name значения используется директива $IF. В логических выражениях директивы $IF могут использоваться большинство логических и арифметических операций Фортрана. Попытка удалить директивой $UNDEFINE символ, который не был определен, приведет к ошибке компиляции. Директивы $DEFINE и $UNDEFINE могут появляться в любом месте программы. Пример: !dec$define tflag real(4) :: tflag = 4 !dec$if defined (tflag) write (*,*) 'Компилирую строку A' !dec$else write (*,*) 'Компилирую строку B' !dec$endif print *, tflag !dec$define tf2 = 2 !dec$if (tf2 == 1) write (*, *) 'Компилирую строку C' !dec$else write (*, *) 'Компилирую строку D' !dec$endif end
! Нет конфликта между именами ! 4.000000 ! или (tf2 .EQ. 1)
Потребность в применении директив условной компиляции возникает на этапе создания исходного кода. Например, используя символическое имя, можно иметь в исходном тексте 2 варианта кода для некоторого фрагмента алгоритма, компилируя тот или иной вариант кода в зависимости от значения введенного директивой имени. После отладки неиспользуемый код может быть удален. Если в директиве $DEFINE имени присваивается целочисленное значение, то такое имя можно использовать в другой директиве $DEFINE для определения значения другого символического имени, например: !dec$define firstsym = 100000
⎯ 265 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
!dec$define receiver = firstsym ! Имя firstsym получило ... ! целочисленное значение в одной из !dec$if receiver .ne. 100000 ! предшествующих метакомад $define write(*, *) "Компилируем эту часть кода" ... !dec$else write(*, *) "Компилируем другую часть кода" ... !dec$endif
П. 1.4.2. Конструкции директив $IF и $IF DEFINED Конструкции директив условной компиляции $IF и $IF DEFINED используются для выполнения условной компиляции. Директивы условной компиляции начинаются с $IF или $IF DEFINED и заканчиваются директивой $ENDIF. Конструкция может включать одну или несколько директив $ELSEIF и одну директиву $ELSE. Если некоторое логическое условие в конструкции вычисляется со значением .TRUE., а все предшествующие логические условия в конструкции $IF вычисляются со значением .FALSE., то выполняется компиляция содержащихся в блоке директивы операторов. Синтаксис директив: cDEC$IF[(]expr[)] или cDEC$IF DEFINED(symbol_name) statementblock [cDEC$ELSEIF[(]expr[)] statementblock] [cDEC$ELSE statementblock] cDEC$ENDIF expr - вычисляемое со значением .TRUE. или .FALSE. логическое выражение. symbol_name - определяемое директивой $DEFINE или опцией компилятора /define символическое имя. В директиве $IF DEFINED проверяется существование символа. Удаление ранее определенного символа выполняется директивой $UNDEFINE. statementblock - операторы программы, которые компилируются или не компилируются в зависимости от значения логических выражений в конструкции $IF. $IF DEFINED(symbol_name) - вычисляется со значением .TRUE., если symbol_name определено директивой $DEFINE (со значением или без) и не удалено директивой $UNDEFINE, в противном случае $IF ⎯ 266 ⎯
Приложение 1. Директивы DVF
DEFINED(symbol_name) вычисляется со значением .FALSE.. Директиве $IF DEFINED (symbol_name) недоступны определенные в тексте программы имена. Если логическое условие директив $IF или $IF DEFINED есть .TRUE., то компилируются операторы следующего за этими директивами statementblock. Если условие вычисляется со значением .FALSE., то управление передается следующим директивам $ELSEIF или $ELSE (при наличии таковых). Таким же образом обрабатывается логическое условие директивы $ELSEIF. Если управление передано директиве $ELSE, то выполняется следующий за директивой statementblock. Наличие закрывающей конструкцию $IF директивы $ENDIF обязательно. В логических выражениях директив можно использовать любые логические операции и операции отношения Фортрана: .LT., , .EQ., ==, .LE., =, .NE., /=, .EQV., .NEQV., .NOT., .AND., .OR. и .XOR.. Логические выражения могут быть любой сложности, но вся директива (включая условие) должна быть размещена на одной строке. Приоритет выполнения логических операций такой же, как и в стандартных выражениях отношения и логических выражениях Фортрана. В выражениях директив условной компиляции логические операции .EQV., .NEQV., .NOT., .AND., .OR. и .XOR. могут быть использованы только с логическими величинами. Пример: !dec$define flag = 3 !dec$if (flag .lt. 2) print *, "Компилирую этот блок, если flag < 2" !dec$elseif (flag >= 8) print *, "Компилирую этот блок, если flag >= 8" !dec$else print *, "Компилирую этот блок, если предшествующие условия - ложь" !dec$endif end
Условная компиляция может быть использована и в модуле. Однако нельзя использовать условную компиляцию для всего модуля в среде DVF, поскольку невозможно будет установить необходимые связи в проекте до компиляции. Например, следующий фрагмент ошибочен: !dec$if defined (debug) module mod real :: b = 3.0
⎯ 267 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
end module !dec$endif
А этот текст компилируется без ошибок: module mod !dec$if defined (debug) real :: b = 3.0 !dec$endif end module
Директивы условной компиляции ($IF, $IF DEFINED, $ELSE, $ELSEIF и $ENDIF) упрощают процесс разработки программы, позволяя пропускать незавершенный код, использовать или пропускать код тестовых вставок, настраивать код для специальных приложений путем подключения и исключения соответствующих фрагментов.
П. 1.5. Управление отладкой программы П. 1.5.1. Директивы $DECLARE и $NODECLARE Директива $DECLARE вырабатывает предупреждение для переменных, если они не появились в операторах объявления типа (работает подобно IMPLICIT NONE). $NODECLARE (устанавливается по умолчанию) отключает выработку предупреждений. Синтаксис: cDEC$DECLARE или cDEC$NODECLARE Директива $DECLARE преимущественно используется на этапах отладки программы для выявления необъявленных либо объявленных, но неиспользующихся переменных.
П. 1.5.2. Директива $MESSAGE Директива посылает символьную строку на стандартное устройство вывода (экран) во время первого прохода компилятора. Предназначена для облегчения процесса отладки. Синтаксис: cDEC$MESSAGE:string string - заключенная в одинарные или двойные кавычки строка сообщений. В среде DVF сообщения выводятся в окно Build. Пример: !dec$message: 'Компиляция подпрограммы setpoint( )'
⎯ 268 ⎯
Приложение 1. Директивы DVF
П. 1.6. Выбор задаваемой по умолчанию разновидности типа П. 1.6.1. Директива $INTEGER Директива устанавливает задаваемое по умолчанию значение параметра разновидности целого типа. Синтаксис: cDEC$INTEGER: 2 | 4 Пользуясь директивой $INTEGER, можно установить задаваемую по умолчанию разновидность целого типа с параметрами разновидности KIND = 2 или KIND = 4. Директива оказывает действие только на объекты данных, объявляемые оператором INTEGER без указания параметра разновидности, и не оказывает действия на буквальные константы и на объекты, введенные без объявления типа. При отсутствии директивы (если не задана опция компилятора /4I2) по умолчанию устанавливается разновидность целого типа с KIND = 4. Директива $INTEGER может появляться только в верхней части программной единицы: головной программы, внешней процедуры, модуля или BLOCK DATA. $INTEGER не может появляться между программными единицами или в начале внутренней процедуры. Директива не воздействует на модули, включаемые оператором USE. Задаваемые по умолчанию разновидности логического и целого типа данных всегда совпадают. Таким образом, действие директивы $INTEGER распространяется и на логические объявленные оператором LOGICAL объекты данных. Пример: integer :: i logical :: f k=2 print *, kind(i), kind(k) print *, kind(fl), kind(.false.) call integer2( ) print *, kind(i), kind(k) end
! 4-байтовая целочисленная переменная ! 4-байтовая логическая переменная ! 4-байтовая целочисленная переменная ! 4 4 ! 4 4
subroutine integer2( ) !dec$integer:2 integer :: j logical :: fl print *, kind(j), kind(3)
! $INTEGER:2 не оказывает действия ! на вызывающую программную единицу ! 2-байтовая целочисленная переменная ! 2-байтовая логическая переменная ! 2 4
!
4
⎯ 269 ⎯
4
О. В. Бартеньев. Visual Fortran: новые возможности
print *, kind(fl), kind(.true.) end subroutine
!
2
4
П. 1.6.2. Директива $REAL Директива устанавливает задаваемое по умолчанию значение параметра разновидности вещественного типа. Синтаксис: cDEC$REAL: 4 | 8 Пользуясь директивой $REAL, можно установить задаваемую по умолчанию разновидность вещественного типа с параметрами разновидности KIND = 4 или KIND = 8. Директива оказывает действие только на объекты данных, объявляемые оператором REAL без указания параметра разновидности, и не оказывает действия на буквальные константы и на объекты, введенные без объявления типа. При отсутствии директивы (если не задана опция компилятора /4R8) по умолчанию устанавливается разновидность вещественного типа с KIND = 4. Директива $REAL может появляться только в верхней части программной единицы: головной программы, внешней процедуры, модуля или BLOCK DATA. Директива $REAL не может появляться между программными единицами или в начале внутренней процедуры. Директива не воздействует на модули, включаемые оператором USE. Пример: real :: r write(*, *) kind(r) call real8( ) write(*, *) kind(r) end subroutine real8( ) !dec$real:8 real :: s write(*, *) kind(s) end subroutine
! 4-байтовая вещественная переменная ! 4 !
4
! 8-байтовая вещественная переменная ! 8
⎯ 270 ⎯
Приложение 1. Директивы DVF
П. 1.7. Управление печатью листинга исходного кода П. 1.7.1. Директива $TITLE Директива задает вывод специального заголовка на каждой странице листинга. Заголовок выводится до тех пор, пока не изменен другой директивой $TITLE. Синтаксис: cDEC$TITLE: title title - символьная буквальная константа. $TITLE может появляться в любом месте исходного кода. Для отключения вывода заголовка следует задать директиву $TITLE, в которой опция title является пустой строкой. При отсутствии директивы никакого заголовка не выводится. Пример: !dec$title: 'Расчет прочности кузова. Версия 2.0 25.11.99' integer i, j, k real a, b, c call track(i, j, k, a, b, c) ...
П. 1.7.2. Директива $SUBTITLE Директива задает вывод специального подзаголовка на каждой странице листинга. Подзаголовок выводится до тех пор, пока не изменен другой директивой $SUBTITLE. Синтаксис: cDEC$SUBTITLE: subtitle subtitle - символьная буквальная константа. При отсутствии директивы никакого подзаголовка не выводится. $SUBTITLE может появляться в любом месте исходного кода. Для отключения вывода заголовка следует задать директиву $SUBTITLE, опция subtitle которой является пустой строкой. Пример: !dec$title: 'Расчет прочности кузова. Версия 2.0 25.11.99 integer(4) :: i, j, k real(4) :: a, b, c call track (i, j, k, a, b, c) call odat (b, c) end
⎯ 271 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
subroutine track (i, j, k, a, b, c) !dec$subtitle: 'Подпрограмма track' integer(4) :: i, j, k real(4) :: a, b, c call stat(a, b) !dec$subtitle: '' end subroutine track
П. 1.8. Директива $OBJCOMMENT Директива помещает полное имя библиотеки с указанием пути в объектный файл. Синтаксис: cDEC$OBJCOMMENT LIB: "library" library - символьная буквальная константа, содержащая имя и при необходимости путь библиотеки, которую должен использовать компоновщик. Компоновщик ищет библиотеку, указанную в $OBJCOMMENT (тот же эффект имеет указание имени библиотеки в командной строке), прежде чем им выполняется поиск установленной по умолчанию библиотеки. Можно в одном исходном файле задать несколько директив, ссылающихся на разные библиотеки. Имена библиотек, которые должен использовать компоновщик, появляются в объектном файле в порядке их введения директивами $OBJCOMMENT в исходном коде. Если директива $OBJCOMMENT появляется в пределах модуля, то каждая использующая модуль программная единица также включает эту директиву. Если необходимо включить директиву в модуль, но не передавать ее в использующие модуль программные единицы, то следует поместить $OBJCOMMENT перед предложением MODULE имя_модуля, например: module a !dec$objcomment lib: "opengl32.lib" ... end module a
! файл mod1.f90
!dec$objcomment lib: "graftools.lib" module b ... end module b
! файл mod2.f90
program go use a объектный код
! файл user.f90 ! Поиск библиотеки
⎯ 272 ⎯
включается
в
Приложение 1. Директивы DVF
use b объектный код ... end
! Поиск библиотеки не включается в ! файла user.obj
П. 1.9. Директива $OPTIONS Директива $OPTIONS управляет выравниванием данных в структурах и общих блоках. Данные указанных объектов могут быть либо выравнены естественным образом, что, как известно, повышает производительность приложения, либо упакованы по произвольным байтовым границам. Синтаксис: cDEC$OPTIONS /[NO]ALIGN ( = p) p - спецификатор, принимающий одно из следующих значений: class = rule, или (class = rule, ...), или ALL, или NONE; class - ключевое слово: COMMONS - для общих блоков; RECORDS и STRUCTURES - для записей; rule - одна из следующих опций: •
PACKED - упаковывает записи или данные общих блоков по произвольным байтовым границам;
•
NATURAL - естественно выравнивает поля записей и данные общих блоков; наибольшее значение байтовой границы выравнивания - 8 байт. Опция несовместима со стандартом Фортрана, но позволяет выравнивать данные с параметром разновидности типа KIND = 8 и комплексные данные с любым значением параметра разновидности;
•
STANDARD - естественно выравнивает поля записей и данные общих блоков; наибольшее значение байтовой границы выравнивания - 4 байта. Опция совместима со стандартом Фортрана. Она применима только с общими блоками, поэтому можно задать /ALIGN = COMMONS = STANDARD, но вызовет ошибку задание правила /ALIGN = STANDARD; ALL - то же, что и задание /ALIGN, или /ALIGN = NATURAL, или /ALIGN = (RECORDS = NATURAL, COMMONS = NATURAL); NONE - то же, что и задание /NOALIGN, или /ALIGN = PACKED, или /ALIGN = (RECORDS = PACKED, COMMONS = PACKED). Директива $OPTIONS и сопровождающая ее директива $END OPTIONS должны следовать после операторов OPTIONS, или SUBROUTINE, или ⎯ 273 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
FUNCTION, или BLOCK DATA, если таковые присутствуют в программной единице, и перед ее исполняемыми операторами. DVF выравнивает данные по естественным границам, однако данные в общих блоках и поля структур могут быть невыравнены (см. гл. 6). Это приводит к замедлению быстродействия программы. Компилятор обнаруживает такие объекты данных и сообщает о них программисту. Выравнивание данных общих блоков достигается действиями: •
задайте /ALIGN = COMMONS = STANDARD для выравнивания по 32битовой границе;
•
задайте /ALIGN = COMMONS = NATURAL для выравнивания по 64-битовой границе;
•
разместите данные в общем блоке в порядке убывания их параметра разновидности типа. При необходимости создания упакованных структур выполните: 1) задайте /ALIGN = RECORDS = PACKED; 2) разместите в структуре объявления данных таким образом, чтобы они оказались выравненными естественным образом. Пример:
! Уровень вложенности директив OPTIONS - 100 !dec$ options /align = packed ! Начало группы А объявления !dec$ options /align = records = natural ! Начало вложенной группы В объявления !dec$ end options ! Конец группы В объявления !dec$ end options ! Конец группы А
Замечание. Директива $OPTIONS в пределах группы В окажет влияние только на структуры; общие блоки в пределах этой группы будут упакованы. Это объясняется тем, что общие блоки сохраняют предшествующие, заданные в группе А установки.
П. 1.10. Директива $PACK Директива $PACK управляет начальными компонентов производных типов. Синтаксис: cDEC$PACK: [1 | 2 | 4]
⎯ 274 ⎯
адресами
памяти
Приложение 1. Директивы DVF
Элементы производных типов данных (структур) и объединений (UNION) выравниваются в памяти ЭВМ двумя способами: по размеру типов элементов или в соответствии с текущими установками выравнивания. Текущие установки выравнивания могут принимать значения 1, 2, 4 или 8 байт. Начальное значение выравнивания памяти может быть выбрано равным 1, 2 или 4 байтам посредством опций компилятора /alignment или /Zp. По умолчанию начальная установка составляет 8 байт. Уменьшая значение установки выравнивания, можно более плотно паковать данные производных типов в памяти компьютера. Директива $PACK позволяет дополнительно управлять упаковкой компонентов производных типов данных внутри программы, изменяя заданную начальную установку выравнивания. Директива может появляться в любом месте программы перед объявлением производного типа данных (структуры) и не может появляться внутри объявлений производных типов. Если директива $PACK не задана, то ко всей программе применяется или установленное по умолчанию значение выравнивания компонентов структур в памяти (8 байт), или значение, заданное опциями компилятора /alignment или /Zp. Задание $PACK:1 означает, что все переменные начинаются на следующем свободном байте (четном или нечетном). Таким образом, между компонентами не образуется свободной памяти, хотя это и приводит к некоторому увеличению времени доступа. Если задана директива $PACK:4 INTEGER(1), LOGICAL(1) и все символьные переменные начинаются на следующем свободном байте (четном или нечетном), INTEGER(2) и LOGICAL(2) начинаются на следующем четном байте, все другие переменные начинаются на 4-байтовой границе. Если директива $PACK: задана без числа, то установка выравнивания возвращается к заданному опциями компилятора /alignment или /Zp значению или к задаваемым по умолчанию 8 байтам. Пример. Выведем расстояние в памяти между двумя компонентами структуры при различных упаковках. Типы компонентов - INTEGER(1) и INTEGER(4). Напомним, что адрес объекта данных возвращается функцией LOC. !dec$pack: 1 type pair1 integer(1) a integer(4) b end type pair1
⎯ 275 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
type (pair1) :: x1 = pair1(3, 3) !dec$pack: 2 type pair2 integer(1) :: a integer(4) :: b end type pair2 type (pair2) :: x2 = pair2(3, 3) !dec$pack: 4 type pair4 integer(1) :: a integer(4) :: b end type pair4 type (pair4) :: x4= pair4(3, 3) !dec$pack: type pair8 integer(1) :: a integer(4) :: b end type pair8 type (pair8) :: x8= pair8(3, 3) print *, loc(x1.b) - loc(x1.a) print *, loc(x2.b) - loc(x2.a) print *, loc(x4.b) - loc(x4.a) print *, loc(x8.b) - loc(x8.a) end
! ! ! !
1 2 4 4
П. 1.11. Директива $PSECT Директива изменяет характеристики общего блока. Имеет синтаксис: cDEC$PSECT /имя общего блока/ a[, a] ... a - одно из следующих ключевых слов: •
ALIGN = val или ALIGN = keyword - задает выравнивание данных общего блока; val - это целочисленная константа, изменяющаяся от 0 до 8 на процессорах Интел и от 0 до 16 на процессорах Альфа; является степенью числа 2; keyword принимает приведенные в табл. П. 1.1 значения. Таблица П. 1.1. Значения константы keyword KEYWORD
Значение
BYTE
0
WORD
1
⎯ 276 ⎯
Приложение 1. Директивы DVF
LONG
2
QUAD
3
OCTA
4
PAGE
9 на Интел и 16 на Альфа
•
GBL (только для VMS) - задает глобальный диапазон видимости;
•
LCL (только для VMS) - задает локальный диапазон видимости; опция противоположна GBL и не может задаваться вместе с ней;
•
[NO]MULTILANGUAGE (только для VMS) - управляет выравниванием размера общего блока для обеспечения совместимости с другими платформами;
•
[NO]SHR (только для VMS) - определяет, могут ли данные общего блока использоваться разными процессами;
•
[NO]WRT (только для VMS) - определяет, можно ли изменять содержимое общего блока во время исполнения программы.
П. 1.12. Директива $ATTRIBUTES Директива $ATTRIBUTES объявляет DEC-атрибуты процедур и переменных. Эти атрибуты являются расширением DVF по отношению к предусмотренным стандартом Фортран 90 атрибутам. Синтаксис: cDEC$ATTRIBUTES attribute-list :: variable-list attribute-list - один или более объявляемых для variable-list атрибутов DEC. При объявлении более одного атрибута последние разделяются запятыми. variable-list - список переменных или имя процедуры или общего блока. При наличии в списке более одного элемента последние разделяются запятыми. Атрибуты DEC упрощают передачу данных и процедур между Фортраном и другим языком программирования, например СИ. Объявление атрибутов должно появляться в разделе объявления данных программной единицы. Атрибуты DEC: ALIAS, C, DLLEXPORT, DLLIMPORT, EXTERN, REFERENCE, STDCALL, VALUE, VARYING. Детальное рассмотрение атрибутов DEC выполнено в гл. 7.
⎯ 277 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
П. 1.13. Директива $ALIAS Директива задает альтернативное внешнее имя для внешней, реализованной на другом языке процедуры. Имеет синтаксис: cDEC$ALIAS внутреннее имя, внешнее имя внутреннее имя - имя процедуры, которое использует Фортран в исполняемом приложении. внешнее имя - имя внешней процедуры; задается либо как имя, написанное прописными буквами, либо как символьная буквальная константа, которая обрамляется двойными или одинарными кавычками. Символьная константа может содержать и прописные и строчные буквы.
П.1.14. Директива IDENT Задает строку, которая используется для идентификации объектного файла. Строка посылается компилятором в идентификационное поле объектного файла для каждой программной единицы. Имеет синтаксис: cDEC$IDENT string string - буквальная символьная константа, содержащая до 31 символа. Замечание. Не следует задавать более одной директивы $IDENT в программной единице, поскольку компилятор воспринимает только первую директиву, а все последующие игнорирует.
П.1.15. Директивы и опции компилятора Некоторые директивы и опции компилятора оказывают одинаковый эффект на компилятор. Список таких директив приведен в табл. П.1.2. Таблица П. 1.2. Эквивалентные директивы и опции компилятора Директива
Эквивалентная опция компилятора
!DEC$DECLARE
/warn:[kyeword] или /4Yd
!DEC$NODECLARE
/warn:none или /4Nd
!DEC$DEFINE symbol
/define:symbol или /Dsymbol
!DEC$INTEGER:option
/integer_size:size или /4Ioption
!DEC$FIXEDFORMLINESIZE:option
/extended_source[:nn] или /4Loption
!DEC$FREEFORM
/free, или /nofixed, или /4Yf
!DEC$NOFREEFORM
/nofree, или /fixed, или /4Nf
⎯ 278 ⎯
Приложение 1. Директивы DVF
!DEC$PACK:option
/alignment:kyeword или /Zpoption
!DEC$REAL:option
/real_size:size или /4Roption
!DEC$STRICT
/std:strict или /4Ys
!DEC$NOSTRICT
/4Ns
Директива в отличие от опции компилятора может оказывать действие на часть программы и при необходимости может быть отключена. Опция компилятора действует во время всего процесса компиляции, если только ее действие не прервано или изменено расположенной в исходном коде директивой. Замечание. Любая директива может начинаться с префикса !MS$.
⎯ 279 ⎯
Приложение 2. Описатели ссылок и размещаемых массивов Фортрана Описатель (ссылки, ссылочного или размещаемого массива) содержит указатель на область памяти и также определяет, как следует осуществлять доступ к этой области после присоединения ссылки к адресату или размещения массива. По своей форме описатель является структурой типа Descriptor (содержащей, в свою очередь, структуру типа DescriptorTriplet) описывающей индексный триплет для каждого измерения массива. Помимо структур приложение содержит код процедур (на Фортране и СИ), позволяющих выполнять некоторые действия с описателями. Так, вычисление адреса размещения первого элемента описателя выполняется функцией DescriptorElementAddress. Прикрепление ссылки осуществляется подпрограммой DescriptorAssign. Приводимые процедуры выполняют те же функции, что и стандартные средства Фортрана. Например, подпрограмма DescriptorAssign выполняет функции оператора прикрепления ссылки =>. Приводимый код поставляется с DVF и находится в подразделе Advanced раздела Samples, расположенного во вкладке Info Viewer.
П. 2.1. Код на Фортране module descript ! Максимальное число измерений массива integer(4), parameter :: DescriptorMaxRank = 7 ! Поля Reserved и Rank являются резервными; в процессе работы ! в некоторых случаях они могут заполняться приложением, ! однако лучше самостоятельно заполнить поле Rank ! для ссылки или массива, а поле Reserved положить равным нулю type DescriptorTriplet integer(4) :: Extent integer(4) :: Mult integer(4) :: LowerBound end type ! Len - длина символьной строки type Descriptor integer(4) :: Base integer(4) :: Len integer(4) :: Offset
! Число элементов в измерении ! Множитель для измерения ! Нижняя граница измерения
! Базовый адрес ! Длина; используется только для строк ! Отступ
⎯ 280 ⎯
Приложение 2. Описатели ссылок и размещаемых массивов Фортрана
integer(4) :: Reserved ! Зарезервировано использования integer(4) :: Rank ! Ранг ссылки type(DescriptorTriplet) Dim(DescriptorMaxRank) end type
для
! Структура используется для передачи данных об измерениях массива ! в подпрограмму DescriptorAssign type DAssign integer(4) :: LowerBound integer(4) :: UpperBound integer(4) :: Stride integer(4) :: RealSize end type ! Значения параметра OrderArg подпрограммы DescriptorAssign logical(4), parameter :: DescriptColumnOrder = .true. logical(4), parameter :: DescriptRowOrder = .false. ! Родовой интерфейс функции DescriptorLoc для базовых типов данных interface DescriptorLoc ! INTEGER(1) для значений компонента Rank в диапазоне [0, 3] integer(4) function DescriptorLoc$I10(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$I10 integer(1), pointer :: p; end function DescriptorLoc$I10 integer(4) function DescriptorLoc$I11(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$I11 integer(1), pointer :: p(:); end function DescriptorLoc$I11 integer(4) function DescriptorLoc$I12(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$I12 integer(1), pointer :: p(:, :); end function DescriptorLoc$I12 integer(4) function DescriptorLoc$I13(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$I13 integer(1), pointer :: p(:, :, :); end function DescriptorLoc$I13 ! INTEGER(4) для значений компонента Rank в диапазоне [0, 3] integer(4) function DescriptorLoc$I40(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$I40 integer(4), pointer :: p; end function DescriptorLoc$I40 integer(4) function DescriptorLoc$I41(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$I41 integer(4), pointer :: p(:); end function DescriptorLoc$I41 integer(4) function DescriptorLoc$I42(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$I42 integer(4), pointer :: p(:, :); end function DescriptorLoc$I42 integer(4) function DescriptorLoc$I43(p)
⎯ 281 ⎯
будущего
О. В. Бартеньев. Visual Fortran: новые возможности
!dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$I43 integer(4), pointer :: p(:, :, :); end function DescriptorLoc$I43 ! REAL(4) для значений компонента Rank в диапазоне [0, 3] integer(4) function DescriptorLoc$R40(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$R40 real(4), pointer :: p; end function DescriptorLoc$R40 integer(4) function DescriptorLoc$R41(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$R41 real(4), pointer :: p(:); end function DescriptorLoc$R41 integer(4) function DescriptorLoc$R42(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$R42 real(4), pointer :: p(:, :); end function DescriptorLoc$R42 integer(4) function DescriptorLoc$R43(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$R43 real(4), pointer :: p(:, :, :); end function DescriptorLoc$R43 ! REAL(8) для значений компонента Rank в диапазоне [0, 3] integer(4) function DescriptorLoc$R80(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$R80 real(8), pointer :: p; end function DescriptorLoc$R80 integer(4) function DescriptorLoc$R81(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$R81 real(8), pointer :: p(:); end function DescriptorLoc$R81 integer(4) function DescriptorLoc$R82(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$R82 real(8), pointer :: p(:, :); end function DescriptorLoc$R82 integer(4) function DescriptorLoc$R83(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$R83 real(8), pointer :: p(:, :, :); end function DescriptorLoc$R83 end interface contains ! Функция DescriptorElementAddress возвращает адрес элемента описателя ! Замечание. Стандартный Фортран может, используя функцию LOC, вычислять ! адрес элемента описателя иным образом. integer(4) function DescriptorElementAddress(dparg, dims) integer(4) dp, dparg integer(4) dims(:) ! Массив размеров type(Descriptor) d pointer(dp, d) integer(4) p ! Результирующий указатель integer(4) r ! Счетчик измерений dp = dparg
⎯ 282 ⎯
Приложение 2. Описатели ссылок и размещаемых массивов Фортрана
if(lbound(dims, 1) /= 1 .or. ubound(dims, 1) > DescriptorMaxRank) then DescriptorElementAddress = -1 return end if p = d%Base + d%Offset do r = 1, ubound(dims, 1) p = p + dims(r) * d%dim(r)%Mult end do DescriptorElementAddress = p end function DescriptorElementAddress ! Подпрограмма DescriptorAssign выполняет операцию прикрепления ссылки ! к адресату. Она работает так же, как и стандартный оператор Фортрана ! прикрепления ссылки (=>) ! Однако она имеет больше возможностей. Так, параметром orderarg учитывается, ! каким образом элементы массива размещены в памяти: по столбцам или по строкам ! Параметры: ! dparg – адрес ссылки Фортрана; возвращается функцией LOC или DescriptorLoc ! base -- базовый адрес, выделяемый ссылке памяти ! size -- значение параметра разновидности типа данных, например 4 для INTEGER(4) ! dims -- массив с данными о каждом измерении ссылки или размещаемого массива ! LowerBound – нижняя граница измерения ! UpperBound—нижняя граница измерения ! Stride -- шаг для измерения ! RealSize -- протяженность измерения ! orderarg (необязательный) – способ размещения в памяти (по столбцам ! или по строкам) ! Пример. Пусть выполнены объявления: ! integer(4), target :: arr(10, 10) ! integer(4), pointer :: dp(:, :) ! Тогда вызов DescriptorAssign выполняется так: ! call DescriptorAssign(DescriptorLoc(dp), loc(arr(1, 1)), 4, & ! (/ DAssign(l1, u1, s1, 10), DAssign(l2, u2, s2, 10) /)) ! Результат такой же, как и после прикрепления ссылки стандартными средствами Фортрана: ! dp => arr(l1:u1:s1, l2:u2:s2) subroutine DescriptorAssign(dparg, base, size, dims, orderarg) integer(4) :: dparg, dp ! dp - целочисленный указатель logical(4), optional :: orderarg logical(4) columnorder
⎯ 283 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
type(DAssign) dims(:) type(Descriptor) d ! Адресная переменная pointer(dp, d) ! Связываем указатель с переменной integer(4) :: r, mult, size, base integer(4) :: todo, dir dp = dparg if(present(orderarg)) then; columnorder = orderarg else; columnorder = .true. end if if(columnorder) then; dir = 1; r = 1 else; dir = -1; r = ubound(dims, 1) end if mult = size d%Base = base d%Offset = 0 d%Len = size d%Rank = ubound(dims, 1) d%Reserved = 0 do todo = 1, ubound(dims, 1) d%Base = d%Base + (dims(r)%LowerBound - 1) * mult d%dim(r)%extent = (dims(r)%UpperBound - dims(r)%LowerBound + & dims(r)%Stride) / dims(r)%Stride if(d%dim(r)%extent < 0) d%dim(r)%extent = 0 d%dim(r)%mult = dims(r)%Stride * mult d%dim(r)%lowerbound = 1 d%Offset = d%Offset - d%dim(r)%mult mult = mult * dims(r)%RealSize r = r + dir end do end subroutine DescriptorAssign ! Подпрограмма DescriptorPrint выводит значения компонентов описателя ! Используется как информационная на этапах отладки приложений subroutine DescriptorPrint(dparg, rank) integer(4) :: dparg, dp type(Descriptor) :: d pointer(dp, d) integer(4) :: r, rank dp = dparg print *, ' Descriptor at Address: ', dparg print *, ' Base Address: ', d%Base print *, ' Length: ', d%Len print *, ' Offset: ', d%Offset
⎯ 284 ⎯
адресной
Приложение 2. Описатели ссылок и размещаемых массивов Фортрана
print *, ' Rank: do r = 1, rank print '(" Dimension ", i1," Extent: print *,' Mult: print *,' LowerBound: end do end subroutine DescriptorPrint
', d%rank ", i12)', r, d%dim(r)%extent ', d%dim(r)%Mult ', d%dim(r)%LowerBound
end module descript
Функция DescriptorLoc возвращает адрес описателя. Она используется в качестве фактического параметра подпрограммы DescriptorAssign, в которую передается не сам описатель, а его адрес. integer(4) function DescriptorLoc(in) integer(4) :: in !dec$attributes value :: in DescriptorLoc = in end function DescriptorLoc
Прежде чем использовать функцию DescriptorLoc, надо проверить, есть ли в родовом интерфейсе DescriptorLoc, приведенном в модуле descript, необходимый специфический интерфейс (родовой интерфейс DescriptorLoc написан для ссылок ранга от 0 до 3 типа INTEGER(1), INTEGER(4), REAL(4) и REAL(8)). Если его там нет, то, заменив надлежащим образом typefoo и type(foo) в нижеприводимом шаблоне, пополните родовой интерфейс DescriptorLoc недостающим специфическим. interface DescriptorLoc ! Шаблон пополнения интерфейса DescriptorLoc integer(4) function DescriptorLoc$typefoo(p) !dec$attributes alias:'_DESCRIPTORLOC@4' :: DescriptorLoc$typefoo type(foo), pointer :: p ! Не забудьте указать ранг ссылки end function end interface
Пример. Сравнить результаты, получаемые приведенными в модуле descript процедурами.
встроенными
program test use descript integer(4), target :: arr(10, 10) ! Тестовые адресат и ссылочный массив integer(4), pointer :: dp(:, :) print *, loc(dp), DescriptorLoc(dp) ! Прикрепим ссылку к адресату, применив встроенный оператор Фортрана dp => arr
⎯ 285 ⎯
и
О. В. Бартеньев. Visual Fortran: новые возможности
call DescriptorPrint(DescriptorLoc(dp), 2) ! Вывод дескриптора nullify(dp) ! Открепляем ссылку от адресата ! Прикрепим ссылку к адресату, использовав подпрограмму DescriptorAssign call DescriptorAssign(DescriptorLoc(dp), loc(arr(1, 1)), 4, & (/ DAssign(1, 10, 1, 10), DAssign(1, 10, 1, 10) /)) call DescriptorPrint(DescriptorLoc(dp), 2) ! Вновь печатаем дескриптор end program test ! Результаты те же
П. 2.2. Код на СИ При работе с дескрипторами Фортрана в СИ можно использовать приводимый ниже код. Пояснения и комментарий к коду размещены в предшествующем разделе. #include <stdarg.h> #include <stdio.h> #define DescriptorMaxRank 7 struct DescriptorTriplet { long Extent; long Mult; long LowerBound; }; struct Descriptor { long Base; long Len; long Offset; long Reserved; long Rank; struct DescriptorTriplet Dim[DescriptorMaxRank]; }; enum {DescriptRowOrder, DescriptColumnOrder}; void *DescriptorElementAddress(struct Descriptor *dp, int rank, ...); void DescriptorAssign(struct Descriptor *dp, void *base, long size, int columnorder, int rank, ...); void DescriptorPrint(struct Descriptor *dp, long rank); // Задайте #define DESCRIPTCODE в одном из файлов, // куда будет включен файл descript.h #ifdef DESCRIPTCODE void *DescriptorElementAddress(struct Descriptor *dp, int rank, ...) { long p; va_list vl; int i;
⎯ 286 ⎯
Приложение 2. Описатели ссылок и размещаемых массивов Фортрана
va_start(vl, rank); p = dp->Base + dp->Offset; for(i = 0; i < rank; i++) p += (dp->Dim[i].Mult * va_arg(vl, long)); va_end(vl); return ((void *) p); } // Пример использования DescriptorAssign в C: // int arr[10][10]; // struct Descriptor dp; // DescriptorAssign(&dp, &arr[0][0], sizeof(arr[0][0]), // DescriptRowOrder, 2, l1, u1, s1, 10, l2, u2, s2, 10); // Приведенный код СИ эквивалентен следующим операторам Фортрана: // integer(4), target :: arr(10,10) // integer(4), pointer :: dp(:, :) // dp => arr(l1:u1:s1,l2:u2:s2) void DescriptorAssign(struct Descriptor *dp, void *base, long size, int columnorder, int rank, ...) { va_list vl; long lbound, ubound, stride, realsize; long mult, r, dir, todo; mult = size; dp->Base = (long) base; dp->Offset = 0; dp->Len = size; dp->Rank = rank; dp->Reserved = 0; if(columnorder) {dir = 1; r = 0;} else {dir = -1; r = rank-1;} va_start(vl, rank); for(todo = 0; todo < rank; todo++) { lbound = va_arg(vl, long); ubound = va_arg(vl, long); stride = va_arg(vl, long); realsize = va_arg(vl, long); dp->Base += ((lbound-1) * mult); dp->Dim[r].Extent = (ubound - lbound + stride) / stride; if(dp->Dim[r].Extent < 0) dp->Dim[r].Extent = 0; dp->Dim[r].Mult = stride * mult; dp->Dim[r].LowerBound = 1; dp->Offset = dp->Offset - dp->Dim[r].Mult; mult *= realsize; r += dir; }
⎯ 287 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
va_end(vl); } void DescriptorPrint(struct Descriptor *dp, long rank) { int r; printf("Descriptor at Address: 0x%08x\n", (void *) dp); printf("Base Address: 0x%08x\n", dp->Base); printf("Length: %d\n", dp->Len); printf("Offset: %d\n", dp->Offset); printf("Rank: %d\n", dp->Rank); for(r = 0; r < rank; r++) { printf(" Dimension %d Extent: %d\n", r, dp->Dim[r].Extent); printf(" Mult: %d\n", dp->Dim[r].Mult); printf(" LowerBound: %d\n", dp->Dim[r].LowerBound); } } #endif
// DESCRIPTCODE
⎯ 288 ⎯
Приложение 3. Вывод русских сообщений в DOS-окно П. 3.1. Преобразования “символ - код символа” и “код символа - код” Пусть в файле work1.txt дана строка, содержащая символы как русского, так и латинского алфавитов. Состав файла work1.txt. Пример символьной строки. An example of a symbol string. Заметим, что каждый символ данной, да и любой, строки имеет код целое положительное число от 1 до 255. Существует также и null-символ, код которого равен нулю. Задача 1. Найти сумму кодов всех символов строки файла work1.txt за вычетом пробелов. Вывести также код каждого символа строки. Используем при решении встроенную функцию IACHAR(c), которая возвращает значение стандартного целого типа, равное коду символа c. Тип параметра c - CHARACTER(1). Длину строки без концевых пробелов найдем функцией LEN_TRIM; i-й символ строки string - это string(i:i). program symbol_codes integer(4) :: i, ico, sco ! sco - искомая сумма кодов символов character(120) :: string character(1) :: ch open(10, file = 'work1.txt') ! Подсоединяем файл к устройству В/В read(10, '(a)') string ! Ввод строки (одной записи) файла do i = 1, len_trim(string) ! LEN_TRIM(string) - возвращает длину ch = string(i:i); ico = iachar(ch) ! строки без концевых пробелов if(ch /= ' ') sco = sco + ico ! Сумма кодов, не включая коды пробелов print *, 'Code of symbol ', ch, ' = ', ico read * ! Ожидаем нажатия Enter end do print *, 'Сумма кодов символов строки без кодов пробелов sco = ', sco end program symbol_codes
Просматривая выводимые данные, мы обнаружим, что пробел имеет код 32, а точка - 46. Буквы английского алфавита имеют код в диапазоне от IACHAR('A') = 65 до IACHAR('z') = 122. Буквы русского алфавита в случае DOS-кодовой страницы 866 имеют код в диапазоне от IACHAR('A') = 128 до ⎯ 289 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
IACHAR('я') = 239. То есть прописные буквы алфавита имеют меньший код, чем соответствующие им строчные буквы. Из сопоставления минимального (128) и максимального кодов (239) букв следует, что в этом диапазоне кодов находятся не только коды русских букв, но и коды иных символов. Коды русских букв в DOS-кодовой странице 866 изменяются в диапазонах: •
128 - 159 - коды прописных букв от А до Я;
•
160 - 175 - коды строчных букв от а до п;
•
224 - 239 - коды строчных букв от р до я. Можно, применив встроенную функцию CHAR(i), выполнить и обратное преобразование: “код символа - символ”. Задача 2. Вывести все буквы русского алфавита. program russian_letters integer(4) :: i character(1) :: ch do i = 128, 128 + 31 ! Вывод прописных букв print '(a, i3, a, a)', 'Capital latter with code ', i, ' - ', char(i) read * ! Ожидаем нажатия Enter end do do i = 160, 160 + 15 ! Вывод первых 16 строчных букв print '(a, i3, a, a)', 'Small letter with code ', i, ' - ', char(i) read * ! Ожидаем нажатия Enter end do do i = 224, 239 ! Вывод следующих 16 строчных букв print '(a, i3, a, a)', 'Small letter with code ', i, ' - ', char(i) read * ! Ожидаем нажатия Enter end do end program russian_letters
П. 3.2. Преобразование DOS-букв русского алфавита в Windows-буквы русского алфавита и обратно Сохраним после ввода с клавиатуры строку текста в файле, использовав, например, такую программу: program string_to_file character(120) string open(10, file = 'work1.txt') read '(a)', string
! Подсоединяем файл к устройству В/В ! Введем: Пример DOS-строки текста.
⎯ 290 ⎯
Приложение 3. Вывод русских сообщений в DOS-окно
write(10, '(a)') string end program string_to_file
! Вывод DOS-строки в файл work1.txt
Откроем файл work1.txt, например, в среде MS Developer Visual Studio, в которой функционирует Digital Visual Fortran или в стандартной Windowsпрограмме NotePad (Блокнот). Тогда введенная в DOS-режиме строка файла work1.txt предстанет в виде следующего набора символов: ЏаЁ¬Ґа DOS-бва®ЄЁ ⥪бв . Задача 3. Преобразовать строку из DOS-представления в Windowsпредставление. Задача 4. Преобразовать строку из Windows-представления в DOSпредставление. Решим прежде промежуточную задачу. Задача 5. Вывести в файл work2.txt Windows-коды букв русского алфавита, принимая во внимание, что Windows-код русской буквы больше ее DOS-кода. Воспользуемся для этого программой: program windows_codes integer(2) :: i open(11, file = 'work2.txt') do i = 160, 255 write(11, '(i4, 2x, a1)') i, char(i) end do end program windows_codes
! Подсоединяем файл к устройству В/В ! Вывод Windows-кодов и символов ! в файл work2.txt
Проанализировав файл work2.txt, мы обнаружим, что коды русских букв в Windows-кодовой странице 1251 изменяются в диапазонах: •
192 - 223 - коды прописных букв от А до Я;
•
224 - 255 - коды строчных букв от а до я. Таким образом, чтобы преобразовать DOS-букву ru_letter русского алфавита в Windows-букву ru_letter русского алфавита потребуется выполнить для букв с DOS-кодами от 128 до 175 (буквы А - Я, а - п) оператор ru_letter = CHAR(ru_letter + (192 - 128)) А для букв с DOS-кодами от 224 до 239 (буквы р - я) следует применить оператор ru_letter = CHAR(ru_letter + (255 - 239)) Понятно, что обратное преобразование потребует уменьшение Windows-кода буквы до соответствующего ее DOS-кода. ⎯ 291 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Оформим преобразования “DOS - Windows” и “Windows - DOS” в виде внешней символьной функции string = ru_doswin(string, dos_win) интерфейс которой опишем в модуле strings_interfaces. Если параметр dos_win функции равен .TRUE., то выполняется преобразование “DOS Windows”, в противном случае (dos_win = .FALSE.) выполняется преобразование “Windows - DOS”. Создадим также модуль strings_consts, в котором объявим константу ncresults, определяющую длину результирующих переменных создаваемых символьных функций. module strings_consts ! Длина строк - результирующих переменных символьных функций integer(4), parameter :: ncresults = 250 end module strings_consts module strings_interfaces обработки строк interface function ru_doswin(string, dos_win) use strings_consts character(ncresults) :: ru_doswin character(*), intent(in) :: string logical(4), intent(in) :: dos_win end function ru_doswin end interface end module strings_interfaces
!
Модуль
с
интерфейсами
процедур
! Параметры string и dos_win имеют вид ! связи IN и, следовательно, не должны ! изменяться в функции ru_doswin
program tasks_3_4 use strings_interfaces ! Ссылка на модуль с интерфейсами character(120) :: string ! Перед выводом текста на консоль выполняем преобразование “Windows - DOS” ! Используем встроенную функцию TRIM для отсечения концевых пробелов print *, trim(ru_doswin('Введите строку на русском языке', .false.)) read(*, '(a)') string ! Ввод DOS-строки с клавиатуры open(11, file = 'work2.txt') ! Вывод Windows-строки в текстовый файл write(11, '(a)') ru_doswin(string, .true.) end program tasks_3_4 ! Файл можно открыть, например, в блокноте function ru_doswin(string, dos_win) use strings_consts character(ncresults) :: ru_doswin character(*), intent(in) :: string
! Получаем доступ к константе ncresults ! Длина строки string не должна превышать ! ncresults символов
⎯ 292 ⎯
Приложение 3. Вывод русских сообщений в DOS-окно
logical(4), intent(in) :: dos_win integer(2) :: i, dos_win_code, dif ! dif - величина, на которую при преобразовании ru_doswin = string ! изменяется код буквы do i = 1, len_trim(ru_doswin) dos_win_code = iachar(ru_doswin(i:i)) ! DOS или Windows код символа dif = 0 ! dif больше нуля, если символ - русская буква if(dos_win) then ! Если преобразование “DOS - Windows” select case(dos_win_code) ! Найдем величину dif case(128 : 175) ! DOS-русские буквы от А до Я и от а до п dif = 64 case(224 : 239) ! DOS-русские буквы от р до я dif = 16 end select else ! Преобразование “Windows - DOS” select case(dos_win_code) case(192 : 239) ! Windows-русские буквы от А до Я и от а до п dif = -64 case(240 : 255) ! Windows-русские буквы от р до я dif = -16 end select end if ! Выполняем преобразование символа, если он является буквой русского алфавита if(dif /= 0) ru_doswin(i:i) = char(dos_win_code + dif) end do end function ru_doswin
Главная программа tasks_3_4 содержит 2 примера использования разработанной функции ru_doswin: 1) вывод после преобразования “Windows - DOS” русского текста на DOSконсоль; 2) вывод после преобразования “DOS - Windows” введенного с клавиатуры русского текста в текстовый файл work2.txt.
⎯ 293 ⎯
Приложение 4. Нововведения стандарта Фортран 95 Стандарт Фортран 95 был опубликован ISO в октябре 1996 года и заместил с того времени Фортран 90. Помимо описания языка стандарт включает перечни удаленных и устаревших свойств Фортрана, которые приведены в двух последних разделах приложения.
П. 4.1. Оператор и конструкция FORALL Оператор и конструкция FORALL, наряду с сечениями массивов и оператором и конструкцией WHERE, используются для выборочного присваивания массивов. FORALL может заменить любое присваивание сечений или WHERE. Но возможности FORALL шире: оператором и особенно конструкцией FORALL можно выполнять присваивания несогласованных массивов, т. е. массивов разной формы. Подобно WHERE и сечениям, FORALL заменяет циклы с присваиванием массивов, например вместо цикла do i = 1, 100 d(i, i) = 2 * g(i) end do
лучше использовать forall(i = 1:100) d(i, i) = 2 * g(i)
Синтаксис оператора: FORALL(спецификация триплета & [, спецификация триплета] ... & [, выражение-маска]) оператор присваивания Синтаксис конструкции: FORALL(спецификация триплета & [, спецификация триплета] ... & [, выражение-маска]) операторы конструкции FORALL END FORALL спецификация триплета имеет вид: индекс = триплет ⎯ 294 ⎯
Приложение 4. Нововведения стандарта Фортран 95
где триплет - это тройка: [нижняя граница]:[верхняя граница]:[шаг]. Каждый из параметров триплета является целочисленным выражением. Шаг изменения индексов может быть и положительным и отрицательным, но не может быть равным нулю. Все параметры триплета являются необязательными. Шаг, если он отсутствует, принимается равным единице. В выражениях, задающих нижнюю, верхнюю границы триплета и его шаг, не должно быть ссылок на индекс. Оценка какого-либо выражения триплета не должна влиять на результат его иного выражения. Индекс - это скаляр целого типа. Область видимости индекса - оператор или конструкция FORALL. После завершения FORALL значение индекса не определено. выражение-маска - логическое выражение-массив; при отсутствии принимается равным .TRUE.. Содержит, как правило, имена индексов, например: forall(i = 1:n, i = 1:n, a(i, j) /= 0.0) b(i, j) = 1.0 / a(i, j)
Переменная, которой в операторе присваивания присваивается значение, должна быть элементом массива или его сечением и содержать имена всех индексов, включенных в спецификации триплетов. Правая часть оператора присваивания не может быть символьного типа. операторы конструкции FORALL - это: • оператор присваивания, обладающий рассмотренными выше свойствами; • оператор или конструкция WHERE; • оператор или конструкция FORALL. Присутствующие в FORALL операторы выполняются для тех значений индексов, задаваемых индексными триплетами, при которых выражениемаска вычисляется со значением .TRUE.. В DO-цикле операторы выполняются немедленно при каждой итерации. FORALL работает иначе: первоначально вычисляется правая часть выражения для всех итераций и лишь затем выполняется присваивание. То же справедливо и для выражений с сечениями, например: integer(4), parameter :: n = 5 integer(4), dimension(n) :: a = 1 integer(4) :: k do k = 2, n a(k) = a(k - 1) + 2 end do print *, a
! Объявляем и инициализируем массив a ! Выполним присваивание в цикле !
1
⎯ 295 ⎯
3
5
7
9
О. В. Бартеньев. Visual Fortran: новые возможности
a=1 forall(k = 2:n) a(k) = a(k - 1) + 2 print *, a a=1 a(2:n) = a(1:n-1) + 2 print *, a
! Присваивание в FORALL ! 1 3 3 3 3 ! Используем выражение с сечениями !
1
3
3
3
3
Ни один из элементов массива не может быть изменен в FORALL более одного раза. Любая процедура, вызываемая в выражении-маске FORALL, должна быть чистой. Любую конструкцию или оператор WHERE можно заменить FORALL, обратное утверждение несправедливо. Примером служит оператор forall(i = 1:n, j = 1:n) h(i, j) = 1.0 / real(i + j)
в котором элементами выражения являются изменяемые индексы, что для WHERE недопустимо. Пример 1: type monarch integer(4), pointer :: p end type monarch type(monarch), dimension(8) :: pattern integer(4), dimension(8), target :: object forall(j=1:8) pattern(j)%p => object(1+ieor(j - 1, 2))
Этот оператор FORALL прикрепляет элементы с номерами 1-8 ссылки pattern соответственно к элементам 3, 4, 1, 2, 7, 8, 5 и 6 адресата object. Встроенная функция IEOR может быть использована, так как она является чистой. Пример 2. Использование конструкции FORALL. forall(i = 3:n + 1, j = 3:n + 1) c(i, j) = c(i, j + 2) + c(i, j - 2) + c(i + 2, j) + c(i - 2, j) d(i, j) = c(i, j) ! Массиву d присваиваются вычисленные в end forall ! предыдущем операторе элементы массива c
Пример 3. Операторы FORALL, которые не заменяются сечениями или WHERE. real(4), dimension(100, 100) :: a = 1.0, b = 2.0 real(4), dimension(300) :: c = 3.0 integer(4) :: i, j forall(i = 1:100, j = 1:100) a(i, j) = (i + j) * b(i, j)
⎯ 296 ⎯
Приложение 4. Нововведения стандарта Фортран 95
forall(i = 1:100) a(i, i) = c(i)
Заметьте, что в последнем случае FORALL обеспечивает доступ к диагонали матрицы, чего нельзя сделать при помощи сечений массивов. Пример 4. Сформировать вектор a из сумм вида
m
∑ x i ,( m = 1, 2, ..., n ). i =1
program vec_a integer(4), parameter :: n = 10 integer(4) i real(4), dimension(n) :: x = (/ (i, i = 1, n) /), a ! Инициализация массива x ! Массив x после инициализации: ! 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 forall(i = 1:n) a(i) = sum(x(1:i)) print '(1x, 10f5.1)', a(1:n) ! 1.0 3.0 6.0 10.0 15.0 21.0 28.0 36.0 45.0 55.0 end program vec_a
Пример 5. Решить методом Гаусса систему линейных уравнений: a11 x1 + a12 x 2 + a13 x 3 + a14 x 4 + a15 = 0 a 21 x1 + a 22 x 2 + a 23 x 3 + a 24 x 4 + a 25 = 0 a 31 x1 + a32 x 2 + a 33 x 3 + a34 x 4 + a35 = 0 a 41 x1 + a 42 x 2 + a 43 x 3 + a 44 x 4 + a 45 = 0 Идея метода Гаусса в том, чтобы исходную систему привести к треугольному виду: * * * * − x1 + a12 x 2 + a13 x 3 + a14 x 4 + a15 = 0 * * * − x 2 + a 23 x 3 + a 24 x 4 + a 25 =0 * * − x3 + a34 x 4 + a 35 =0 * − x 4 + a 45 =0
Процесс приведения называется прямым ходом. После приведения выполняется обратный ход, в результате которого вычисляются искомые значения неизвестных: сначала x4, затем x3, затем x2 и в последнюю очередь x1. Алгоритм выполнения обратного хода тривиален, поэтому будет опущен. Реализацию прямого хода рассмотрим на примере системы уравнений
⎯ 297 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
2 x1 + 3x 2 + 11x 3 + 5x 4 − 2 = 0 x1 + x 2 + 5 x 3 + 2 x 4 − 1 = 0 2 x1 + x 2 + 3x 3 + 2 x 4 + 3 = 0 x1 + x 2 + 3x 3 + 4 x 4 + 3 = 0 Выразим x1 через другие неизвестные, разделив первое уравнение на –a11 = –2: x1 = –1,5x2 – 5,5x3 – 2,5x4 + 1. (*) Теперь, заменив x1 в трех оставшихся уравнениях системы на правую часть уравнения (*), мы получим систему: − x1 − 1,5x 2 − 5,5x 3 − 2,5x 4 + 1 = 0 − 0,5x 2 − 0,5x 3 − 0,5x 4 =0 − 2 x 2 − 8 x 3 − 3x 4 + 5 = 0 − 0,5x 2 − 2,5x 3 + 1,5 x 4 + 4 = 0 Понятно, что на следующем шаге следует взять второе уравнение и выразить x2 через x3 и x4, а затем заменить в третьем и четвертом уравнениях x2 на полученное выражение. Повторив далее эти операции для x3, мы получим систему − x1 − 1,5x 2 − 5,5x3 − 2,5x 4 + 1 = 0
− x2 −
x3 − − x3 −
x4
=0
1 5 x4 + = 0 6 6 − x4 − 1 = 0
Выполнив обратный ход, найдем корни x4 = -1, x3 = 1, x2 = 0, x1 = -2. В приводимой ниже программе, реализующей метод Гаусса, искомые корни заносятся в массив x. Коэффициенты и свободные члены системы уравнений хранятся в массиве a. Функция RESHAPE преобразовывает одномерный массив, задаваемый посредством конструктора, в двумерный массив формы (n, n + 1). Параметр order = (/ 2, 1 /) функции обеспечивает запись каждых n + 1 элементов конструктора в соответствующую строку массива-результата. При его отсутствии формирование массива-результата будет выполнено по столбцам. Массив b, в котором запоминается исходная матрица, введен для выполнения проверки вычислений. Суть проверки в том, что произведение матрицы коэффициентов уравнения и найденного
⎯ 298 ⎯
Приложение 4. Нововведения стандарта Фортран 95
вектора x (выполняется встроенной функцией MATMUL) должно быть равно вектору, состоящему из свободных членов уравнения. В отлаженной программе матрица b, конечно же, избыточна. Компактность программ обеспечивается не только применением встроенных функций, но и заменой DO-циклов с присваиваниями массива сечениями массива. program Gauss integer(4), parameter :: n = 4 real(8) :: a(n, n + 1), b(n, n+1) ! Используем двойную точность real(8) :: x(n) = 0.0 a = reshape((/ 2, 3, 11, 5, -2, & 1, 1, 5, 2, -1, & 2, 1, 3, 2, 3, & 1, 1, 3, 4, 3 /), & shape = (/ n, n + 1 /), order = (/ 2, 1 /)) b=a do i = 1, n - 1 ! Прямой ход a(i, i:) = -a(i, i:) / a(i, i) ! Используем сечение a(i, i:). Записи a(i, i:) forall(j = i+1:n) ! и a(i, i:n+1) эквивалентны a(j, i+1:) = a(j, i+1:) + a(j, i) * a(i, i+1:) end forall if(i > 1) a(i, :i-1) = 0.0 ! Этот оператор может отсутствовать end do a(n, n:) = -a(n, n:) / a(n, n) ! Завершаем прямой ход a(n, :n-1) = 0.0 ! Этот оператор может отсутствовать x(n) = a(n, n+1) ! Выполняем обратный ход do i = n - 1, 1, -1 a(i, i+1:n) = a(i, i+1:n) * x(i+1:n) x(i) = sum(a(i, i+1:)) end do print '(4f9.3)', x print '(4f9.6)', matmul(b(:, :n), x) - pack(-b(:, n+1:n+1), .true.) end program Gauss
Функция PACK возвращает одномерный массив (вектор) из свободных членов исходного уравнения. В выполняющем прямой ход DO-цикле присутствует конструкция FORALL, которая заменяет цикл do j = i + 1, n a(j, i+1:) = a(j, i+1:) + a(j, i) * a(i, i+1:) end do
Конструкция FORALL будет работать быстрее, чем DO-цикл. Это, однако, обнаружится в том случае, если вы работаете на ⎯ 299 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
многопроцессорной машине и используете, например, компилятор Digital Visual Fortran (DVF) фирмы Digital с дополнительным программным обеспечением (Digital Parallel Software Environment), позволяющим распараллеливать вычисления. То же справедливо и для конструкций (операторов) WHERE и для выражений, в которых используются сечения массивов.
П. 4.2. Оператор ELSEWHERE Теперь конструкция WHERE может включать оператор ELSEWHERE, например: where(a >= 0.0) b = 3.0 + sin(a) - sum(a) elsewhere b = 0.0 end where
Приведенный код эквивалентен следующему: t1 = sum(a) do i = 1, n if(a(i) >= 0.0) b(i) = 3.0 + sin(a) - t1 else b(i) = 0.0 end if end do
П. 4.3. Чистые процедуры Чистыми называются процедуры, не имеющие побочных эффектов. Пример побочного эффекта демонстрирует программа program side_effect real(4) :: dist, d, p = 3.0, q = 4.0, r = 5.0 d = max(dist(p, q), dist(q, r)) print *, d end program side_effect function dist(p, q) real(4) :: dist, p, q dist = sqrt(p * p + q * q) q = dist end function dist
!
7.071068
! Изменение q - побочный эффект
⎯ 300 ⎯
Приложение 4. Нововведения стандарта Фортран 95
Суть его в том, что функция dist переопределяет значение параметра q. А это означает, что второй вызов функции dist при вычислении d выполняется при q, равном 5.0, возможно вместо ожидаемого первоначального значения q = 4.0. Такие эффекты запрещены стандартом и должны отслеживаться и устраняться программистом. Сообщение о том, что процедура является чистой, обеспечивается ключевым словом PURE, применяемым в заголовке процедуры: [type-spec] PURE SUBROUTINE | FUNCTION name [RESULT (resultname)] & или PURE [type-spec] SUBROUTINE | FUNCTION name [RESULT (resultname)] & type-spec - тип результирующей переменной функции. name - имя процедуры. resultname - имя результирующей переменной функции. Чистая процедура характеризуется тем, что: • •
функция возвращает значение и не меняет ни одного из своих параметров; подпрограмма изменяет только те параметры, которые имеют вид связи INTENT(OUT) и INTENT(INOUT). По умолчанию чистыми являются:
•
все встроенные функции и встроенная подпрограмма MVBITS;
•
процедуры библиотеки высокоскоростного Фортрана, применяемого для паралльных вычислений под Юниксом.
В чистых процедурах все формальные параметры, кроме формальных процедур и ссылок, должны иметь вид связи: •
для функций - только INTENT(IN);
•
для подпрограмм - любой INTENT(IN, или OUT, или INOUT).
Никакие локальные переменные чистой процедуры, в том числе и относящиеся к внутренним процедурам, не должны: •
обладать атрибутом SAVE;
•
быть инициализированными в операторах объявления или DATA. В чистых процедурах имеются ограничения на использование:
•
глобальных переменных; ⎯ 301 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
•
формальных параметров с необъявленным видом связи;
•
объектов, ассоциируемых по памяти с какими-либо глобальными переменными.
Ограничения использоваться:
таковы:
видом
связи
перечисленные
INTENT(IN)
объекты
не
или
с
должны
•
в случаях, когда возможно изменение их значения. Это может произойти, если переменная является: • левой частью оператора присваивания или прикрепления ссылки (если объект является ссылкой); • фактическим параметром, ассоциированным с формальным параметром с видом связи INTENT(OUT или INOUT) или обладающим атрибутом POINTER; • индексной переменной операторов DO, FORALL или встроенного DO-цикла; • переменной оператора ASSIGN; • элементом списка ввода оператора READ; • именем внутреннего файла оператора WRITE; • объектом операторов ALLOCATE, DEALLOCATE или NULLIFY; • спецификаторами IOSTAT или SIZE операторов В/В или STAT операторов ALLOCATE и DEALLOCATE;
•
в создании ссылки, например, в качестве адресата или в качестве элемента правой части оператора присваивания переменной производного типа, если он имеет ссылочный компонент на любом из его уровней. Чистые процедуры не должны содержать:
•
операторов В/В во внешние файлы или устройства;
•
операторы PAUSE и STOP.
Чистые процедуры предназначены для вызова в тех случаях, когда вызов иных, не владеющих ключевым словом PURE, процедур недопустим: •
в операторе FORALL или его выражении-маске;
•
из другой чистой процедуры. Также только чистую процедуру можно использовать в качестве параметра другой чистой процедуры. ⎯ 302 ⎯
Приложение 4. Нововведения стандарта Фортран 95
Если чистая процедура используется в приведенных ситуациях, то ее интерфейс должен быть задан явно и она должна быть объявлена в нем с ключевым словом PURE. Напомним, что все встроенные процедуры являются чистыми и по умолчанию обладают явным интерфейсом. Пример: pure function decr(k, m) real(4) :: decr integer(4), intent(in) :: k, m decr = real(m) / real(k) end function decr
! Формальные параметры чистой функции ! должны иметь вид связи INTENT(IN)
program pudem real(4), dimension(5, 5) :: array = 5.0 interface pure function decr(k, m) ! Поскольку функция используется FORALL, real(4) :: decr ! то необходимо задать ее интерфейс integer(4), intent(in) :: k, m end function decr end interface forall(i = 1:5, j = 1:5) array(i, j) = decr(i, j) print '(10f5.1)', array(1, :) ! 1.0 2.0 3.0 4.0 5.0 end program pudem
в
П. 4.4. Элементные процедуры Элементные пользовательские процедуры подобно встроенным элементным процедурам могут иметь в качестве фактических параметров либо скаляры, либо массивы. В последнем случае массивы должны быть согласованы, т. е. иметь одинаковую форму; результатом процедуры является поэлементная обработка массивов - фактических параметров. Приведем пример выполнения встроенной элементной функции MOD, возвращающей остаток от деления первого параметра на второй: integer(4), dimension(5) :: a = (/ 1, 2, 3, 4, 5 /), b = (/ 1, 2, -2, 4, 3 /), c integer(4) :: d c = mod(a, b) ! Параметры функции - массивы print *, c ! 0 0 1 0 2 d = mod(b(4), a(3)) ! Параметры функции - скаляры print *, d ! 1
Программная единица, вызывающая элементную функцию, должна содержать ее интерфейс, в котором явно указано слово ELEMENTAL. ⎯ 303 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
Цель введения элементных функций - упростить распараллеливание вычислений на многопроцессорных машинах: компилятор, имеющий сведения о том, что функция элементная, выполняет распараллеливание по заложенным в него правилам. Элементные функции - это чистые функции, имеющие только скалярные формальные параметры, не являющиеся ссылками или процедурами. Вид связи параметров - INTENT(IN). Результирующая переменная элементной функции также является скаляром и не может быть ссылкой. Элементная функция снабжается ключевым словом ELEMENTAL, которое автоматически подразумевает ключевое слово PURE. Элементные функции не могут быть оснащены ключевым словом RECURSIVE. Если фактическими параметрами элементной функции являются массивы, то они должны быть согласованы; результатом такой функции является массив, согласованный с массивами-параметрами. Например: elemental integer(4) function find_c(a, b) ! Не забываем задать вид связи INTENT(IN) integer(4), intent(in) :: a, b if(a > b) then find_c = a else if(b < 0) then find_c = abs(b) else find_c = 0 end if end function find_c
program etest interface ! Интерфейс обязателен elemental integer(4) function find_c(a, b) ! Обязательное задание вида связи integer(4), intent(in) :: a, b end function find_c ! INTENT(IN) end interface integer(4), dimension(5) :: a = (/ -1, 2, -3, 4, 5 /), b = (/ 1, 2, -2, 4, 3 /), c integer(4) :: d = 5 c = find_c(a, b) ! Параметры функции - массивы print *, c ! 0 0 2 0 5 d = find_c(-1, 1) ! Параметры функции - скаляры print *, d ! 0 end program etest
Замечание. Поскольку элементные функции являются чистыми, они могут быть использованы в операторе и конструкции FORALL.
⎯ 304 ⎯
Приложение 4. Нововведения стандарта Фортран 95
Элементные подпрограммы задаются подобно элементным функциям. В теле процедуры могут изменяться параметры с видом связи OUT и INOUT. Пример: elemental subroutine find_c(a, b, c) integer(4), intent(in) :: a, b integer(4), intent(out) :: c if(a > b) then c=a else if(b < 0) then c = abs(b) else c=0 end if end subroutine find_c
program etest2 interface elemental subroutine find_c(a, b, c) integer(4), intent(in) :: a, b integer(4), intent(out) :: c end subroutine end interface integer(4), dimension(5) :: a = (/ -1, 2, -3, 4, 5 /), b = (/ 1, 2, -2, 4, 3 /), c integer(4) :: d = 5 call find_c(a, b, c) ! Параметры и результат - массивы print *, c ! 0 0 2 0 5 call find_c(-1, 1, d) ! Параметры и результат - скаляры print *, d ! 0 end program etest2
П. 4.5. Встроенные функции MINLOC и MAXLOC Функции для массивов MAXLOC и MINLOC дополнены необязательным параметром dim и имеют теперь синтаксис: MAXLOC(array [, dim] [, mask]) и MINLOC(array [, dim] [, mask]) MAXLOC - возвращает индексы максимального элемента массива array или максимальных элементов по заданному измерению dim. Значение каждого максимального элемента удовлетворяет заданным (необязательным) условиям mask. Если несколько элементов содержат максимальное значение, то берется первый по порядку их следования в ⎯ 305 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
array. Результат MAXLOC, если не задан параметр dim, записывается в одномерный массив, размер которого равен числу измерений array. Если параметр dim задан, то: •
массив-результат имеет ранг, на единицу меньший ранга массива array, и форму (d1, d2, ..., ddim-1, ddim+1, ..., dn), где (d1, d2, ..., dn) - форма массива array;
•
если array имеет ранг, равный единице, то MAXLOC(array, dim [, mask]) возвращает то же, что и функция MAXLOC(array [, MASK = = mask]); иначе значение элемента (s1, s2, ..., sdim-1, sdim+1, ..., sn) результата функции MAXLOC(array, dim [, mask]) равно MAXLOC(array(s1, s2, ..., sdim-1, sdim+1, ..., sn), [,MASK = mask(s1, s2, ..., sdim-1, sdim+1, ..., sn)]); MINLOC - выполняет те же действия, что и MAXLOC, но для минимальных элементов массива array. Пример:
integer(4), parameter :: m = 3, n = 5 real(4) :: a(m, n) integer ip(n) a = reshape((/ 3.0, 4.0, 5.0, 6.0, 7.0, 2.0, 3.0, 4.0, 5.0, 6.0, & 1.0, 2.0, 3.0, 4.0, 5.0 /), & shape = (/ m, n /), order = (/ 2, 1 /)) ip = maxloc(array = a, mask = a < 5, dim = 1) print *, ip ! 1 1 2 3 0
&
П. 4.6. Расширение функций CEILING и FLOOR Функции для массивов CEILING и FLOOR имеют теперь необязательный параметр kind и выполняются следующим образом: CEILING(a, [kind]) - возвращает наименьшее целое, большее или равное значению вещественного аргумента a. Разновидность типа результата совпадает со значением аргумента kind, если он задан, или - в противном случае - со стандартной разновидностью целого типа; FLOOR(a, [kind]) - возвращает наибольшее целое, меньшее или равное значению вещественного аргумента a. Разновидность типа результата совпадает со значением аргумента kind, если он задан, или - в противном случае - со стандартной разновидностью целого типа. Пример: integer(4) :: i
⎯ 306 ⎯
Приложение 4. Нововведения стандарта Фортран 95
integer(2) :: iarray(2) i = ceiling(8.01) iarray = floor((/ 8.01, -5.6 /), kind = 2)
! Возвращает 9 типа INTEGER(4) ! Возвращает (8, -6) типа INTEGER(2)
П. 4.7. Инициализация ссылки и функция NULL Ссылку можно инициализировать, применив функцию NULL: real(4), dimension(::), pointer :: pa => null( )
Функция NULL дает ссылке статус "не ассоциирована с адресатом". Этот статус позволяет, например, использовать ссылку в качестве фактического параметра до ее прикрепления к адресату, например: program null_test real(4), dimension(:), pointer :: pa => null( ) interface subroutine poas(pa) real(4), dimension(:), pointer :: pa end subroutine poas end interface call poas(pa) ! Параметр - неприкрепленная ссылка print *, pa(2) ! 3.500000 end program null_test subroutine poas(pa) real(4), dimension(:), pointer :: pa allocate(pa(5)) pa = 3.5 end subroutine poas
Функция NULL может быть использована и среди исполняемых операторов: pa => null( )
П. 4.8. Инициализация компонентов производного типа Компонентам производного типа при его объявлении можно присвоить начальные значения, которые по умолчанию будут являться начальными значениями соответствующих компонентов всех объектов этого типа. Инициализированы могут быть как все, так и отдельные компоненты, например: type entry real(4) :: val = 3.0
! Объявление типа entry ! Инициализация компонента val
⎯ 307 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
integer(4) :: index type(entry), pointer :: next => null( ) end type entry type(entry) :: erray(10) print *, erray(5)%val
! Инициализация не выполняется ! Инициализация компонента next !
3.000000
П. 4.9. Встроенная подпрограмма CPU_TIME Подпрограмма CPU_TIME(time) возвращает процессорное время time, тип которого - REAL(4). Единицы измерения времени - секунды; после десятичной точки time содержит две значащие цифры. Может быть использована для оценки продолжительности вычислений, например: real(4) :: start_time, finish_time call cpu_time(start_time) call cpu_time(finish_time) print *, 'Calculation time = ', finish_time - start_time
П. 4.10. Автоматическое освобождение размещаемых массивов Если при выходе из процедуры приложение явно оператором DEALLOCATE не освобождает занимаемую размещаемым массивом память, то теперь это произойдет автоматически. Это новое свойство приводит к снижению недоступной, ранее выделенной под размещаемые массивы памяти.
П. 4.11. Комментарии в NAMELIST-списке Именованный, предназначенный для ввода список с данными теперь может содержать комментарий, следующий, как и в исходном коде, после восклицательного знака, например: integer(4) :: k, iar(5) logical(4) :: fl real(4) :: r4 complex(4) :: z4 character(10) :: c10 namelist /mesh/ k, fl, r4, z4, c10, c4, iar open(1, file = 'a.txt') read (1, mesh) write(*, *) k, iar, fl
⎯ 308 ⎯
Приложение 4. Нововведения стандарта Фортран 95
write(*, *) r4, z4, ' ', c10
Состав файла a.txt: &Mesh K = 100, FL = T, Z4 = (38, 0), ! Z4 - переменная типа COMPLEX(4) C10 = 'abcdefgh' ! Значение символьной переменной r4 = 24.0, iar = 1, 2, 3, 5, 5 /
П. 4.12. Вычисляемая длина поля при форматном выводе В Фортране, если при форматном выводе число полученных в результате преобразования символов превосходит длину поля w, все поле заполняется звездочками (*). Теперь, однако, можно задать значение w, равное нулю, например I0 или F0.5. В этом случае длина поля определяется значением выводимого числа. Это свойство применимо с дескрипторами B, F, I, O и Z.
П. 4.13. Полная версия оператора END INTERFACE Ранее, в стандарте Фортран 90, родовой интерфейс мог быть задан так: interface mymax module procedure inmax, remax, chmax end interface
Теперь же END INTERFACE может завершаться родовым именем: interface mymax module procedure inmax, remax, chmax end interface mymax
П. 4.14. Исключенные из Фортрана свойства Стандарт 95-го года исключил из Фортрана: 1) DO-цикл с вещественным и двойной точности параметром, например запрещен цикл: real(4) :: x, xs = 1.0, xf = 3.0, dx = 0.1 do x = xs, xf, df print *, x*sin(x) end do
2) переход на END IF из внешнего блока; ⎯ 309 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
3) оператор PAUSE; 4) оператор ASSIGN присваивания меток и назначаемый GO TO; ясно, что теперь нельзя использовать в качестве метки целочисленную переменную, получившую значение в результате выполнения оператора ASSIGN: integer :: label, m = 55 assign 20 to label print label, m 20 format(1x, i5)
! ASSIGN - исключенный оператор ! Ошибка. Использование label недопустимо
5) символьные константы с указателем длины, называемые также холлеритовскими константами, например: 16HThis is a string.
П. 4.15. Устаревшие свойства Фортрана Приводимые ниже устаревшие свойства языка будут удалены из Фортрана в последующих версиях, и, следовательно, их применение нежелательно. В приводимом перечне тремя звездочками (***) отмечены свойства, которые, начиная с версии Фортран 95, классифицируются как устаревшие. Прочие свойства отнесены к устаревшим еще стандартом Фортран 90. В списке 9 устаревших свойств: 1) арифметический оператор IF; 2) завершение нескольких DO-циклов одним оператором и завершение DO-цикла оператором, отличным от CONTINUE или END DO; 3) альтернативный возврат из процедуры; 4) вычисляемый оператор GO TO (***); 5) операторная функция (***); 6) размещение оператора DATA среди исполняемых операторов (***); 7) символьные функции предполагаемой - CHARACTER(len = *) - длины (***); 8) фиксированный формат исходного кода (***); 9) форма CHARACTER* для объявления символьных типов данных (***). Предполагается изъять из Фортрана первые 6 свойств уже в следующем стандарте.
⎯ 310 ⎯
Литература 1) Бартеньев О. В. Современный Фортран. - М.: Диалог-МИФИ, 1998. 397 с. 2) Бартеньев О. В. Фортран для студентов. - М.: Диалог-МИФИ, 1999. 400 с. 3) Березин Б. И., Березин С. Б. Начальный курс С и С++. - М.: ДиалогМИФИ, 1999. - 288 с. 4) Боресков А. В., Шикин Е. В., Шикина Г. Е. Компьютерная графика: первое знакомство. - М.: Финансы и статистика, 1996. - 176 с. 5) Любимский Э. З., Мартынюк В. В., Трифонов Н. П. Программирование. - М.: Наука, 1980. - 608 с. 6) Меткалф М., Рид Дж. Описание языка программирования Фортран 90. М.: Мир, 1995. - 302 с. 7) Пильщиков В. Н. Программирование на языке ассемблера IBM PC. - М.: Диалог-МИФИ, 1999. - 288 с. В. А. Язык С++ и объектно-ориентированное 8) Скляров программирование. - Минск: Высш. шк., 1997. - 478 с. 9) Фортран 90. Международный стандарт. - М.: Финансы и статистика, 1998. - 416 с. 10) Шикин Е. В., Боресков А. В. Компьютерная графика. Динамика, реалистические изображения. - М.: Диалог-МИФИ, 1995. - 288 с.
⎯ 311 ⎯
Содержание ПРЕДИСЛОВИЕ ................................................................................................3 1. ИСПОЛЬЗОВАНИЕ ДИАЛОГОВ ..............................................................5
1.1. ПОСТАНОВКА ЗАДАЧИ ...............................................................................5 1.2. ПОСТРОЕНИЕ ДИАЛОГОВОГО ОКНА ...........................................................6 1.2.1. Проект для диалога.................................................................................. 6 1.2.2. Задание параметров диалога .................................................................. 6 1.2.3. Задание и обработка статического текста ........................................ 7 1.2.4. Обработка редактируемых полей .......................................................... 9 1.2.5. Кнопки OK и Cancel................................................................................ 10 1.2.6. Меню диалога.......................................................................................... 11 1.2.7. Доступ к файлам хранения диалога...................................................... 12 1.2.8. Работа с диалогом в программе ........................................................... 13
1.3. УСОВЕРШЕНСТВОВАНИЕ ПРОГРАММЫ
ТАБУЛЯЦИИ ФУНКЦИИ ..............17
1.3.1. Вывод сообщения.................................................................................... 17 1.3.2. Задание числа итераций ........................................................................ 18 1.3.2.1. Зачем группировать радиокнопки ............................................ 18 1.3.2.2. Элементы управления, задающие число итераций ................. 19 1.3.2.3. Устройство элементов управления ......................................... 20 1.3.2.4. Использование радиокнопок ...................................................... 20 1.3.2.5. Изменения в тексте программы .............................................. 21
1.4. УПРАВЛЯЮЩИЕ ЭЛЕМЕНТЫ ДИАЛОГА ....................................................23 1.5. ПРОЦЕДУРЫ ДЛЯ РАБОТЫ С ДИАЛОГОМ...................................................24 1.6. УПРАВЛЯЮЩИЕ ИНДЕКСЫ.......................................................................25 1.7. ПРИМЕНЕНИЕ СПИСКОВ ...........................................................................29 1.7.1. Открытые списки.................................................................................. 29 1.7.2. Списки с редактируемым полем ........................................................... 35 1.7.3. Список без редактируемого поля.......................................................... 38
1.8. ВЫХОД ИЗ ДИАЛОГА ................................................................................39 1.9. ИЗМЕНЕНИЕ ВОЗВРАЩАЕМОЙ ДИАЛОГОМ ВЕЛИЧИНЫ ...........................40 2. ВЫВОД ГРАФИЧЕСКИХ ДАННЫХ ......................................................41
2.1. ГРАФИЧЕСКИЙ ДИСПЛЕЙ .........................................................................41 2.2. РАСТРОВОЕ ИЗОБРАЖЕНИЕ ......................................................................42 2.3. ВИДЕОАДАПТЕР .......................................................................................43 2.4. ВИДЕООКНО И ОКНА ВЫВОДА..................................................................44 ⎯ 312 ⎯
Содержание
2.5. ЗАДАНИЕ КОНФИГУРАЦИИ ВИДЕООКНА ..................................................45 2.6. СИСТЕМЫ ГРАФИЧЕСКИХ КООРДИНАТ. ОКНО ВЫВОДА ..........................48 2.7. ОЧИСТКА И ЗАПОЛНЕНИЕ ЭКРАНА ЦВЕТОМ ФОНА ..................................53 2.8. УПРАВЛЕНИЕ ЦВЕТОМ .............................................................................54 2.8.1. Система цветов RGB. Цветовая палитра .......................................... 54 2.8.2. Цветовая палитра VGA......................................................................... 56 2.8.3. Не RGB-функции управления цветом ................................................... 60 2.8.3.1. Управление цветом фона .......................................................... 60 2.8.3.2. Управление цветом неграфического текста .......................... 62 2.8.3.3. Управление цветом графических примитивов ........................ 63 2.8.4. RGB-функции управления цветом ......................................................... 63 2.8.4.1. Управление RGB-цветом фона ................................................. 63 2.8.4.2. Управление RGB-цветом неграфического текста ................. 64 2.8.4.3. Управление RGB-цветом графических примитивов ............... 64
2.9. ТЕКУЩАЯ ПОЗИЦИЯ ГРАФИЧЕСКОГО ВЫВОДА ........................................65 2.10. ГРАФИЧЕСКИЕ ПРИМИТИВЫ ..................................................................66 2.10.1. Вывод пикселей ..................................................................................... 67 2.10.2. Вывод отрезка прямой линии .............................................................. 72 2.10.3. Вывод прямоугольника ......................................................................... 72 2.10.4. Вывод многоугольника ......................................................................... 73 2.10.5. Вывод эллипса и окружности ............................................................. 74 2.10.6. Вывод дуги эллипса и окружности ..................................................... 75 2.10.7. Вывод сектора...................................................................................... 76 2.10.8. Координаты конечных точек дуги и сектора ................................... 77 2.10.9. Пример вывода графических примитивов.......................................... 77
2.11. ВЫВОД ТЕКСТА ......................................................................................79 2.11.1. Вывод текста без использования шрифтов ...................................... 79 2.11.2. Вывод зависимого от шрифта текста ............................................. 82
2.12. УПРАВЛЕНИЕ ТИПОМ ЛИНИЙ .................................................................87 2.13. ЗАПОЛНЕНИЕ ЗАМКНУТЫХ ОБЛАСТЕЙ ...................................................89 2.14. ПЕРЕДАЧА ОБРАЗОВ ...............................................................................92 2.14.1. Обмен с оперативной памятью .......................................................... 92 2.14.2. Обмен с внешней памятью .................................................................. 98
2.15. СТАТУС ВЫПОЛНЕНИЯ ГРАФИЧЕСКИХ ПРОЦЕДУР ...............................100 3. ПРИЛОЖЕНИЯ QUICKWIN ..................................................................102
3.1. ВОЗМОЖНОСТИ QUICKWIN ................................................................102 3.2. ОПЕРАЦИИ НАД ОКНАМИ QUICKWIN .................................................103 3.2.1. Виды окон QUICKWIN ......................................................................... 103 3.2.2. Создание дочернего окна ..................................................................... 103 3.2.3. Активизация дочернего окна ............................................................... 104
⎯ 313 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
3.2.4. Размещение дочернего окна в фокусе................................................. 105 3.2.5. Закрытие устройства дочернего окна .............................................. 105 3.2.6. Изменение свойств дочернего окна .................................................... 105 3.2.7. Изменение размеров и позиции обрабляющего и дочернего окна..... 106
3.3. ИЗМЕНЕНИЕ СИСТЕМНОГО МЕНЮ..........................................................111 3.4. ИНИЦИАЛИЗАЦИЯ МЕНЮ И ОБРАМЛЯЮЩЕГО ОКНА .............................114 3.5. СОЗДАНИЕ СПИСКА ИМЕЮЩИХСЯ ДОЧЕРНИХ ОКОН .............................116 3.6. ИМИТАЦИЯ ВЫБОРА КОМАНД МЕНЮ .....................................................117 3.7. ИЗМЕНЕНИЕ СООБЩЕНИЙ QUICKWIN.................................................118 3.8. ВЫВОД СТАНДАРТНОГО ОКНА СООБЩЕНИЙ ..........................................118 3.9. ПЕРЕОПРЕДЕЛЕНИЕ СООБЩЕНИЯ О ПРОГРАММЕ ...................................120 3.10. КОПИРОВАНИЕ ТЕКСТА И ГРАФИКИ ОКНА QUICKWIN......................120 3.11. ПРИМЕНЕНИЕ ПОЛЬЗОВАТЕЛЬСКИХ ИКОН ...........................................122 3.12. ИСПОЛЬЗОВАНИЕ МЫШИ .....................................................................123 3.12.1. Связанные с мышью события ........................................................... 123 3.12.2. Функции обработки событий ........................................................... 124 3.12.3. Блокирующая функция WAITONMOUSEEVENT .............................. 128 3.12.4. Особенности работы с блокирующими процедурами .................... 131 3.12.5. Особенности подпрограмм обработки событий............................ 132
4. МНОГОНИТОЧНОЕ ПРОГРАММИРОВАНИЕ ................................133
4.1. ПОСТАНОВКА ЗАДАЧИ ...........................................................................133 4.2. НИТИ И ПРОЦЕССЫ ................................................................................135 4.3. ОРГАНИЗАЦИЯ НИТЕЙ ............................................................................136 4.3.1. Модули для многониточного программирования .............................. 136 4.3.2. Построение проекта с несколькими нитями .................................... 137 4.3.3. Создание нити ...................................................................................... 137 4.3.4. Реализующая нить процедура ............................................................. 138 4.3.5. Пример создания нити......................................................................... 139 4.3.6. Использование значения параметра argument................................... 140 4.3.7. Обеспечение независимости переменных процедуры нити............. 141 4.3.8. Способы синхронизации нитей при доступе к ресурсам ................. 141
4.4. ПРОГРАММИРОВАНИЕ ОБЪЕКТОВ СИНХРОНИЗАЦИИ НИТЕЙ .................142 4.4.1. Критические секции ............................................................................. 142 4.4.2. Текст программы вывода бегущих полос с использованием критической секции ....................................................................................... 144 4.4.3. Устранение недостатка в работе программы BARS2 .................... 146 4.4.4. Исключения ........................................................................................... 147 4.4.5. Применение исключений в рассматриваемой задаче ........................ 149 4.4.6. Семафоры ............................................................................................. 150 4.4.7. Применение семафоров в рассматриваемой задаче ......................... 151
⎯ 314 ⎯
Содержание
4.5. ОРГАНИЗАЦИЯ НИТЕЙ ПРИ МНОГООКОННОМ ВЫВОДЕ .........................152 4.6. ПЕРЕЧЕНЬ МНОГОНИТОЧНЫХ ПРОЦЕДУР ...............................................158 5. КОМПИЛЯЦИЯ И ПОСТРОЕНИЕ ПРОГРАММ ..............................161
5.1. НАЗНАЧЕНИЕ КОМАНДЫ DF ..................................................................161 5.2. ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ ....................................................................161 5.3. ФОРМАТ КОМАНДЫ DF..........................................................................163 5.4. ПРАВИЛА ЗАДАНИЯ ОПЦИЙ ...................................................................164 5.5. ВХОДНЫЕ И ВЫХОДНЫЕ ФАЙЛЫ ............................................................164 5.6. ФОРМИРОВАНИЕ ИМЕН ВЫХОДНЫХ ФАЙЛОВ ........................................165 5.7. ВРЕМЕННЫЕ ФАЙЛЫ ..............................................................................165 5.8. УПРАВЛЕНИЕ БИБЛИОТЕКОЙ ОБЪЕКТНЫХ ФАЙЛОВ...............................166 5.9. ВАРИАНТЫ ИСПОЛЬЗОВАНИЯ КОМАНДЫ DF.........................................167 5.9.1. Компиляция и построение с одним исходным файлом ..................... 167 5.9.2. Применение переменной окружения DF ............................................ 168 5.9.3. Компиляция и построение с несколькими исходными файлами....... 168 5.9.4. Использование последовательности команд ..................................... 169 5.9.5. Подключение библиотек объектных файлов ..................................... 170 5.9.6. Использование динамических библиотек ........................................... 170 5.9.7. Компиляция и построение приложений с текстами программ на Фортране и СИ................................................... 171 5.9.8. Оптимизация при компиляции и построении .................................... 175 5.9.9. Команда DF, параметры которой хранятся в текстовом файле ....................................................................................... 175 5.9.10. Примеры ошибочного использования команды DF ......................... 176
5.10. ОГРАНИЧЕНИЯ КОМПИЛЯТОРА И ПОСТРОИТЕЛЯ .................................176 5.11. ПЕРЕЧЕНЬ ОПЦИЙ КОМПИЛЯТОРА И ПОСТРОИТЕЛЯ ............................177 5.12. РАСПРЕДЕЛЕНИЕ ОПЦИЙ ПОСТРОИТЕЛЯ ПО КАТЕГОРИЯМ VS .....................................................................................186 5.13. ИСПОЛЬЗОВАНИЕ ОПЦИЙ FPS В КОМАНДЕ DF....................................188 6. ПОВЫШЕНИЕ БЫСТРОДЕЙСТВИЯ ПРОГРАММ .........................193
6.1. ВВЕДЕНИЕ ..............................................................................................193 6.2. ВРЕМЯ ВЫПОЛНЕНИЯ ПРОГРАММЫ .......................................................193 6.3. ВЫРАВНИВАНИЕ ДАННЫХ .....................................................................195 6.3.1. Размещение данных в памяти............................................................. 195 6.3.2. Невыравненные данные........................................................................ 196 6.3.3. Сообщения о невыравненных данных ................................................. 197 6.3.4. Как выравнивать данные..................................................................... 197 6.3.5. Опции компилятора, управляющие выравниванием .......................... 198
⎯ 315 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
6.4. ОПТИМИЗАЦИЯ ИСХОДНОГО КОДА ........................................................199 6.4.1. Эффективное использование массивов .............................................. 199 6.4.2. Организация быстрого ввода/вывода................................................. 202 6.4.3. Дополнительные приемы оптимизации кода..................................... 204
6.5. ВЛИЯНИЕ ОПЦИЙ КОМАНДЫ DF НА ПРОИЗВОДИТЕЛЬНОСТЬ ................205 6.6. ОБОБЩЕНИЯ...........................................................................................210
⎯ 316 ⎯
Содержание
7. ПРОГРАММИРОВАНИЕ НА НЕСКОЛЬКИХ ЯЗЫКАХ ................211
7.1. ВВЕДЕНИЕ ..............................................................................................211 7.2. АТРИБУТЫ DEC.....................................................................................213 7.2.1. Атрибут ALIAS .................................................................................... 214 7.2.2. Атрибуты С и STDCALL ..................................................................... 215 7.2.3. Атрибут EXTERN ................................................................................ 218 7.2.4. Атрибут REFERENCE......................................................................... 220 7.2.5. Атрибут VALUE................................................................................... 221 7.2.6. Атрибут VARYING............................................................................... 222 7.2.7. Атрибуты DLLEXPORT и DLLIMPORT............................................. 223
7.3. СОГЛАШЕНИЯ ОБ ИМЕНАХ .....................................................................225 7.4. ПРОПИСНЫЕ И СТРОЧНЫЕ БУКВЫ В ИМЕНАХ ........................................227 7.4.1. Имена из прописных букв..................................................................... 227 7.4.2. Имена из строчных букв...................................................................... 228 7.4.3. Имена из смеси прописных и строчных букв ..................................... 229 7.4.4. Имена VISUAL C++ ............................................................................. 229
7.5. ИНТЕРФЕЙС ВНЕШНЕЙ ПРОЦЕДУРЫ.......................................................230 7.6. СОГЛАСОВАНИЕ ТИПОВ ДАННЫХ ..........................................................231 7.7. ПЕРЕДАЧА ДАННЫХ В ПРОГРАММАХ С РАЗНОЯЗЫЧНЫМИ ПРОЦЕДУРАМИ233 7.7.1. Обмен данных через параметры процедур ........................................ 233 7.7.1.1. Передача символьных данных ................................................. 235 7.7.1.2. Передача массивов................................................................... 238 7.7.1.3. Передача ссылок и размещаемых массивов Фортрана........ 241 7.7.1.4. Передача целочисленных указателей ..................................... 242 7.7.1.5. Имена модулей.......................................................................... 243 7.7.1.6. Доступ к объектам модулей Фортрана в функциях СИ ....................................................................................... 244 7.7.1.7. Определение модульной процедуры в СИ............................... 246 7.7.2. Использование common-блоков Фортрана и структур СИ ............................................................................................... 246 7.7.2.1. Прямой доступ к common-блокам Фортрана и структурам СИ ................................................................................. 246 7.7.2.2. Передача адреса common-блока.............................................. 248 7.7.3. Передача производных типов данных ................................................ 249
7.8. ОСОБЕННОСТИ ОДНОВРЕМЕННОГО ИСПОЛЬЗОВАНИЯ ФОРТРАНА И СИ ..............................................................251 7.9. ВКЛЮЧЕНИЕ ФОРТРАН-ПРОЦЕДУР В ПРИЛОЖЕНИЯ НА БЕЙСИКЕ ..................................................................................................251 7.10. СОЗДАНИЕ ПРИЛОЖЕНИЙ НА ФОРТРАНЕ И АССЕМБЛЕРЕ ...................252 7.10.1. Формирование результата функцией Ассемблера.......................... 253
⎯ 317 ⎯
О. В. Бартеньев. Visual Fortran: новые возможности
7.10.2. Примеры программ на Фортране и Ассемблере ............................. 253
ПРИЛОЖЕНИЕ 1. ДИРЕКТИВЫ DVF .......................................................259 П. 1.1. ОБЗОР ДИРЕКТИВ ...............................................................................259 П. 1.2. ИСПОЛЬЗОВАНИЕ ДИРЕКТИВ .............................................................260 П. 1.3. ДИРЕКТИВЫ, КОНТРОЛИРУЮЩИЕ ПРАВИЛА НАПИСАНИЯ ИСХОДНОГО КОДА .............................................................................................................262 П. 1.3.1. Директивы $STRICT и $NOSTRICT................................................ 262 П. 1.3.2. Директивы $FREEFORM и $NOFREEFORM................................ 263 П. 1.3.3. Директива $FIXEDFORMLINESIZE .............................................. 264
П. 1.4. УСЛОВНАЯ КОМПИЛЯЦИЯ ПРОГРАММЫ ...........................................264 П. 1.4.1. Директивы $DEFINE и $UNDEFINE ............................................. 264 П. 1.4.2. Конструкции директив $IF и $IF DEFINED................................. 266
П. 1.5. УПРАВЛЕНИЕ ОТЛАДКОЙ ПРОГРАММЫ .............................................268 П. 1.5.1. Директивы $DECLARE и $NODECLARE ...................................... 268 П. 1.5.2. Директива $MESSAGE.................................................................... 268
П. 1.6. ВЫБОР ЗАДАВАЕМОЙ ПО УМОЛЧАНИЮ РАЗНОВИДНОСТИ ТИПА .................................................................................269 П. 1.6.1. Директива $INTEGER ..................................................................... 269 П. 1.6.2. Директива $REAL............................................................................ 270
П. 1.7. УПРАВЛЕНИЕ ПЕЧАТЬЮ ЛИСТИНГА
ИСХОДНОГО КОДА ...................271
П. 1.7.1. Директива $TITLE ........................................................................... 271 П. 1.7.2. Директива $SUBTITLE.................................................................... 271
П. 1.8. ДИРЕКТИВА $OBJCOMMENT .........................................................272 П. 1.9. ДИРЕКТИВА $OPTIONS ...................................................................273 П. 1.10. ДИРЕКТИВА $PACK........................................................................274 П. 1.11. ДИРЕКТИВА $PSECT ......................................................................276 П. 1.12. ДИРЕКТИВА $ATTRIBUTES ..........................................................277 П. 1.13. ДИРЕКТИВА $ALIAS.......................................................................278 П.1.14. ДИРЕКТИВА IDENT .........................................................................278 П.1.15. ДИРЕКТИВЫ И ОПЦИИ КОМПИЛЯТОРА .............................................278 ПРИЛОЖЕНИЕ 2. ОПИСАТЕЛИ ССЫЛОК И РАЗМЕЩАЕМЫХ МАССИВОВ ФОРТРАНА.....................................280 П. 2.1. КОД НА ФОРТРАНЕ ............................................................................280 П. 2.2. КОД НА СИ........................................................................................286 ПРИЛОЖЕНИЕ 3. ВЫВОД РУССКИХ СООБЩЕНИЙ В DOS-ОКНО ..................................................................................................289
⎯ 318 ⎯
Содержание
П. 3.1. ПРЕОБРАЗОВАНИЯ “СИМВОЛ - КОД СИМВОЛА” И “КОД СИМВОЛА - КОД” ..............................................................................289 П. 3.2. ПРЕОБРАЗОВАНИЕ DOS-БУКВ РУССКОГО АЛФАВИТА В WINDOWSБУКВЫ РУССКОГО АЛФАВИТА И ОБРАТНО ...................................................290 ПРИЛОЖЕНИЕ 4. НОВОВВЕДЕНИЯ СТАНДАРТА ФОРТРАН 95...................................................................................................294 П. 4.1. ОПЕРАТОР И КОНСТРУКЦИЯ FORALL..............................................294 П. 4.2. ОПЕРАТОР ELSEWHERE .................................................................300 П. 4.3. ЧИСТЫЕ ПРОЦЕДУРЫ .........................................................................300 П. 4.4. ЭЛЕМЕНТНЫЕ ПРОЦЕДУРЫ ................................................................303 П. 4.5. ВСТРОЕННЫЕ ФУНКЦИИ MINLOC И MAXLOC ..............................305 П. 4.6. РАСШИРЕНИЕ ФУНКЦИЙ CEILING И FLOOR..................................306 П. 4.7. ИНИЦИАЛИЗАЦИЯ ССЫЛКИ И ФУНКЦИЯ NULL................................307 П. 4.8. ИНИЦИАЛИЗАЦИЯ КОМПОНЕНТОВ ПРОИЗВОДНОГО ТИПА ...............307 П. 4.9. ВСТРОЕННАЯ ПОДПРОГРАММА CPU_TIME .....................................308 П. 4.10. АВТОМАТИЧЕСКОЕ ОСВОБОЖДЕНИЕ РАЗМЕЩАЕМЫХ МАССИВОВ 308 П. 4.11. КОММЕНТАРИИ В NAMELIST-СПИСКЕ .........................................308 П. 4.12. ВЫЧИСЛЯЕМАЯ ДЛИНА ПОЛЯ ПРИ ФОРМАТНОМ ВЫВОДЕ ..............309 П. 4.13. ПОЛНАЯ ВЕРСИЯ ОПЕРАТОРА END INTERFACE ..........................309 П. 4.14. ИСКЛЮЧЕННЫЕ ИЗ ФОРТРАНА СВОЙСТВА......................................309 П. 4.15. УСТАРЕВШИЕ СВОЙСТВА ФОРТРАНА..............................................310 ЛИТЕРАТУРА ................................................................................................311
⎯ 319 ⎯