FORTRAN 90 程序编程规范
Fortran 90 编程规范,使程序代码高度组织化,更加易读、易懂、易于维护,程序更加高效。使编出的程序更易懂、易于维护。
1 语言选择
数值预报创新系统软件开发应避免使用Fortran77 的某些过时特征以Fortran 90不一致的特征。选择Fortran 90 作为开发语言,并采用Fortran 90 的新功能,如动态内存的分配(dynamic memory allocation)、递归(recursion ), 模块(modules)、POINTER 、长变量名、自由格式等。
Fortran 77其中某些只是一些冗余的功能,这些功能已经过时,另外,还有一些在Fortran90 中被证明是不好的用法,建议不要使用。
2 Fortran 90 的新特性
2.1.1 建议使用的Fortran 90 新特性
建议使用Fortran 90 提供的模块(module ),并用Use ONLY 指定module 中哪些变量或派生类型定义可用于调用程序。
尽量使用数组下标三元组,这样可优化并减少所需的代码行数。为提高可读性,要在括号内表明数组的维数,例如:
1dArrayA(:) = 1dArrayB(:) + 1dArrayC(:)
2dArray(: , :) = scalar * Another2dArray(: , :)
当访问数组的子集时,例如在有限差分等式中,可以通过使用下标三元组实现。例如:2dArray(: , 2:len2) = scalar *( &
Another2dArray(:, 1:len2 -1) &
- Another2dArray(:, 2:len2) &
)
对程序单元(program units )命名,并使用End program ,End subroutine ,End interface ,End module 等结构再次指定“program unit ”的名称。
在逻辑表达式中使用>、 >=、 ==、 <、 <=、 /=, 它们分别代替.gt.、.ge.、.eq.、.lt.、.le.、.ne. 。新的表示方法更接近标准的数学符号
在变量定义中始终使用“::”;始终用“DIMENSION ”定义数组形状;始终用(len=)的语法格式声明字符变量的长度。
1
从效率的角度考虑,除递归定义的数据结构外,应尽量避免使用递归程序(它在使用内存和cpu 上会降低效率)。
建议定义新的操作符,而不要重载已有的操作符,这样可以清晰地表明功能,避免降低代码的可读性和可维护性。 2.1.2 关于Interface
使用Interface 接口块。在FORTRAN 语言中,主调程序与被调程序是分别编译的。由于Fortran 90 对过程的许多功能做了扩充,有些功能单靠简单的调用语句已无法反映,因而系统也就无法进行正确的编译。这时需要在主调程序中加入interface 接口块,通过它为主调程序与被调程序指明一个显式的接口。如果被调用程序中哑元含有假定形状(assumed-shape )数组,或含可选变元,或含键盘输入的参数,就需要interface 接口块说明。一般来讲,在Fortran 90 程序之间需要提供的interface 块,建议将interface 接口块明确写入调用程序,并复制被调用程序的参数列表。这种方法简单易用,但也相应增加了维护代码的工作量,因为只要被调用程序的参数列表发生变化就必须相应改变interface 接口块和调用(call )语句。 2.1.3 关于动态内存的使用
并行程序中存在着动态内存使用不充分的情况,合理使用动态内存可提高效率。在Fortran90 中常采用下列方法获取动态内存。
自动数组(automatic arrays):这种数组的大小在子程序中指定,其大小取决于运行时的变量,例如,可通过参数列表向子程序传递实元变量。使用自动数组优先于其它形式的动态内存分配方法。
可分配数组(allocatable arrays):声明为ALLOCATABLE 属性的数组变量可在运行时通过ALLOCATE 命令分配空间。与指针不同的是ALLOCATABLE 属性不能用于派生数据类型中。这种方法申请的空间要使用DEALLOCATE 命令释放。
在一个程序中,不要重复进行“ALLOCATE—DEALLOCATE-然后再ALLOCATE 一个更大的空间”这样的操作,这样会导致大量不可利用的碎片空间产生。应在程序中对要开辟的动态数组空间进行连续分配,或连续释放。
在使用ALLOCATE 和DEALLOCATE 命令之后,要利用ALLOCATE 和DEALLOCATE 命令参数中的返回值来判断操作是否成功;不建议使用指针数组。 2.2 Fortran 90 中不建议使用的过时的特性
不要使用固定书写格式,采用Fortran 90 的自由书写格式。
不要使用旧式的DO 循环和CONTINUE 语句。在以前的DO 循环中,关键字之后大多有一个标号,循环在该标号处结束,如: DO 10 I = 1, 10
2
DO 20 j= 1, 20 A(i,j)=0. 20 CONTINUE? 10 CONTINUE?
建议采用统一的DO 结构,如: DO i = 1, 10 DO j = 1, 20 A(i, j) = 0. END DO END DO
不能使用实循环变量?
在DO 循环中若用实数作循环变量,由于截断误差的存在,会导致得不到预期的循环次数,如应将下面的DO 循环 DO r = 0.1 ,0.9 ,0.1 ... END DO 改写为:
DO i = 1 , 9 r = i * 0.1 ... END DO
不要使用DO WHILE 语句,可用不带循环变量的DO 语句配合exit 替代,如: 不要使用选择性返回。例如:
CALL Foo( x, y, *100, *200, *300 )
在执行完该子程序后程序根据Foo 的返回语句后的表达式值选择从标号为100 、200 、
300 处继续执行。可将上述子程改写为结构化CASE 语句: CALL Foo(x, y, return_code) SELECT CASE(return_code) ...
不要使用算法IF 语句:使用块IF 结构来代替;
不要使用计算GO TO 语句,如GO TO (10,20,30) i ; 不要使用标号赋值语句,如 ASSIGN 300 i
3
GO TO i
不要使用除IMPLICIT NONE 之外的任何implicit 语句,如 IMPLICIT REAL(a-z)
不要使用DATA 语句,对变量赋初值可在声明时进行,或在程序中进行。 DATA 语句是唯一可用于对变量赋二进制、八进制、十六进制初值的语句,但它很可能
造成程序的不可移植,因此除非必须这样做,一般建议不要采用。
不要使用COMMON 公共块:使用Modules 参数列表代替公共块向子程序传递数据;
不要使用EQUIVALENCE 等价语句:Fortran90 中,由于模块、动态存储、指针、数据结构以及固有函数transfer 的引用,没有必要继续使用EQUIVALENCE 语句;
不要使用数据块程序单元DATABLOCK ,在FORTRAN 77 中它用于给公用块内数据赋初值。
不要使用FORMAT 语句。
不要使用err= ,end= 及eor= 等描述符。在输入输出语句中,这些描述符后面跟语句标号,指示如果出现错误则程序转向执行标号所标识的语句。我们可用检查iostat 来替代,如:
READ ( * , ”(A)” , iostat=status ) line 不要使用语句函数,如 f(x) = x**2 - 1
可用contain 内部函数子程序来代替。 不要使用假定大小数组
它是作为哑元使用的,它的最后一维无上界,而用*代替,如 DIMENSION a(100,*)。
在Fortran 90 中可用假定形状数组代替,它的每一维都是可调的,并用(:)来声明, 如:
REAL , DIMENSION( 100 , : ) :: a
不要使用FORTRAN 的INCLUDE 行。在多数情况下可用USE 来代替,有些则采用预处
理#include 来代替。
不能使用双精度类型,采用Fortran 90 的种别参数。 不要使用PAUSE 语句;
不要使用ENTRY 语句:一个子程序只能有一个入口点;Fortran90 中避免
4
数组在子程序之间传递时维数的隐式改变。 尽量不要采用经销商扩充的功能。 2.3 在FORTRAN 90 中限制使用的功能
GO TO 语句和语句标号只能在程序出现异常情况需要立即退出时才能使用,即允许使用GO TO 语句的唯一例外,是在出现错误时跳到程序的末尾处,这时用9999 作为Labels
(这样可使所有人都知道GO TO 9999 的意思)。 2.4 关于预编译器(Precompilers )的使用
建议在数值预报创新系统中使用预编译器,因为它提供了一种可有选择的对程序系统的某一部分程序进行编译的方法(条件编译);另外,为了提高程序的可移植性,同样也需要使用预编译器。由于超级计算机都是用UNIX 操作系统,C 预编译器(cpp, C language pre-processor)可能是最好的选择。 2.4.1 文件的包含
Fortran 90 提供了利用“INCLUDE ”语句引入文件的功能,但有时需要加入文件的目录结构信息,因此可以改用cpp 中的#include 功能,是程序更加清晰。“include ”后的文件可以是用于变量定义、子程序注释或interface 接口块等的一段程序。如, SUBROUTINE Foo IMPLICIT NONE ...
INTERFACE
#include
CALL TypePackageIni #endif ...
END SUBROUTINE Foo
将视type_package_init 的定义调用子程序TypePackageIni 。
3可读性与可维护性
本节将主要介绍如何使Fortran 90 程序更易读、更易维护。 3.1 空白的使用
空白的使用可以使程序更易读、易维护。加入空白的方法主要有空行、空格。
5