F90中,共有四种程序单元:主程序、过程或辅程序、块数据单元和模块。下表是对它们的概括描述:
程序单元
|
定义
|
主程序
|
主程序是程序开始执行的标志,其第一条语句不能是SUBROUTINE,FUNCTION,MODULE和BLOCK DATA。主程序可以用PROGRAM语句作为第一条语句,但不是必需的
|
过程
|
子程序或函数
|
块数据单元
|
在命名的公共块中提供变量初始值的程序单元
|
模块
|
包含数据对象定义、类型定义、函数或子程序接口和其它程序可访问的函数或子程序
|
主程序是整个程序的入口,类似于C里的main函数。它的格式为:
[PROGRAM [程序名]]
[说明部分]
[可执行部分]
[CONTAINS
内部过程]
END [PROGRAM[程序名]]
可以看到主程序里可以有一个内部过程(在后面介绍)。
过程类似于C语言里的函数。它分为外部过程、内部过程和内在过程。
外部过程独立于其它的程序单元,它的形式有两种:
外部函数:FUNCTION语句
[说明部分]
[可执行部分]
[CONTAINS
内部过程]
END [FUNCTION函数名]
外部子程序:SUBROUTINE语句
[说明部分]
[可执行部分]
[CONTAINS
内部过程]
END [SUBROUTINE子程序名]
内部过程是(通过contains语句)包含在宿主单元(外部过程、模块或主程序单元)内的过程,它的形式也有两种:
内部函数 :FUNCTION语句
[说明部分]
[可执行部分]
END [FUNTION函数名]
内部子程序:SUBROUTINE语句
[说明部分]
[可执行部分]
END [SUBROUTINE子程序名]
可以看到内部过程不能再包含其它的内部过程。
内在过程就是Fortran的库函数,比如sin、abs等。Fortran90中定义了113个内在过程。
下面是子程序与过程(外部与内部)的例子:
- program procedures
- implicit none
- integer:: x=5,y=6,res=0
- integer:: add !声明函数(返回值)
- character*8 get_something
- call increase(x, y) !必须用call来调用一个
- print *, x, y ! 6, 7
- res = add(x,y)
- print *, x, y, !7,8,15
- print *, get_something() !hello
- end
- subroutine increase(x,y) !x,y为哑元,类似于C的参数,但是是引用
- integer:: x,y; !声明实元,与哑元对应起来。类似于实参和形参的关系。
- x = x + 1;
- y = y + 1;
- end
- function add(x,y) result(s) !s为返回值
- integer:: x,y,s;
- x = x+1;
- y = y+1;
- s = x+y;
- end
- function get_something() !不声明result则函数名自身为返回值名
- character*8 get_something;
- call inner(get_something);
- contains
- subroutine inner(s) !内部过程
- character*8 s
- s = "hello"
- end subroutine
- end
External属性和哑过程用来调用外部文件里的过程,分别对应于C的extern关键字和外部函数声明。下面是一个例子:
caller.f:
- program caller
- integer,external:: add
- print *, add(1,2)
- end
callee.f:
- function add(x,y) result(s)
- integer,intent(in):: x,y !被声明为intent(in)的参数不能被修改
- integer:: s
- s = x + y
- end
除了哑过程,还可以用
interface来声明外部过程:
- program caller
- interface
- function add(x,y)
- integer::x,y,add
- end function
- end interface
- print *, add(1,2)
- end
变元的save属性类似于c的static,而且在未声明的情况下变量
也都是save的。(而且我的gfortran(gcc4.5.2)根本不支持automatic属性,哪怕是用-fautomatic和
-frecursive的命令参数。这样所有的变量都只能是save的……fortran95本应该支持static关键字的,但我的gfortran也
不支持……)。
在调用子过程或函数时,可以使用关键字变元,使用关键字改变传入实参的顺序。比如:
- program caller
- interface
- subroutine test_keyword_param(A,B,C,D)
- integer A,B,C,D
- end
- end interface
- call test_keyword_param(62,C=83,D=7,B=2)
- end
- subroutine test_keyword_param(A, B, C, D)
- integer A, B, C, D
- print *, "A=", A;
- print *, "B=", B;
- print *, "C=", C;
- print *, "D=", D;
- end
在使用关键字变元时,必须在调用端定义一个接口。
另一个有趣的特性是可选变元,它用optional属性表示。代码如下:
- subroutine test_optional_param(A, B, C, D)
- integer A, B
- integer, intent(in), optional :: C, D
- print *, present(C)
- !logical, intrinsic :: present
- if (present(C)) then
- print *, C, "as C is passed in!";
- end if
- if (present(D)) then
- print *, D, "as D is passed in!";
- end if
- end
通过call test_optional_param(1,2)可以调用它。(我的gfortran编译出来的东西不管C,D有没有传入,present都返回true。真是2)
如果函数要支持
递归的话,必须在函数前显式声明recursive属性,而且还必须有显式result声明。
通过接口可以把几个子过程合并成一个类属过程,类似于C里的模板的作用:
- interface show
- subroutine show_integer(x)
- integer x
- end
- subroutine show_real(x)
- real x
- end
- end interface
- call show (55);
- call show (32.13);
- end
- subroutine show_integer(x)
- integer x
- print *, "integer - ", x
- end
- subroutine show_real(x)
- real x
- print *, "real - ", x
- end
块数据单元用来存放公共数据,它的形式为:
BLOCK DATA[块数据名]
[说明部分]
END [BLOCK DATA[块数据名]]
推荐使用模块来存放公共数据,而非DATA。
Fortran中所有程序单元都是分别编译的,所以它们的作用域都是独立的。也就是说,一个程序单元内部的局部变量是不会被其它程序单元使用的。Fortran77使用了COMMON和EQUIVALENCE来共享数据。
COMMON用于在不同的程序单元中共享数据的用法如下:
- program common_test
- common /group1/a,b,c
- integer a,b,c
- a = 5
- b = 6
- c = 7
- call print_commons
- end
- subroutine print_commons
- common /group1/x,y,z
- integer x, y, z
- print *, x, y, z !5,6,7
- end
EQUIVALENCE用于在同一程序单元中共享数据:
- program equivalence_test
- integer a, b, c
- character*8 e, f
- equivalence (a, b, c), (e, f) !a,b,c共享数据;e,f共享数据
- a = 5;
- print *, a, b, c !5,5,5
- b = 10;
- print *, a, b, c !10,10,10
- e = "ok"
- print *, e, f !ok,ok
- end
INCLUDE是一种共享代码(而非共享数据)的方法:
- program interface_test
- include 'mytype.f'
- type(mytype) :: t1 = mytype(1,1.)
- end
mytype.f的定义为:
- type mytype
- integer a
- real b
- end type
在Fortran90引入了
模块MODULE的概念,它包含了COMMON、EQUIVALENCE和INCLUDE的功能。它是共享数据推荐使用的方式:
- module mymodule
- integer global
- integer, private:: hidden
- type mytype
- integer a
- real b
- end type
- type, private:: hiddentype
- integer x
- real y
- end type
- contains
- subroutine show
- print *, global, hidden
- end subroutine
- end module
- program interface_test
- use mymodule
- interface
- function add(x,y) result(z)
- use mymodule
- type(mytype), intent(in) :: x, y
- type(mytype) :: z
- end function add
- end interface
- type(mytype) :: t1 = mytype(1,1.)
- type(mytype) :: t2 = mytype(2,2.)
- type(mytype) :: t3
- ! type(hiddentype) :: ht = hiddentype(5,0.5) !不能访问private type
- global = 88
- hidden = 99
- call show !88, 0 private数据访问无效
- t3 = add(t1, t2)
- print *, t3%a, t3%b !3,3.0
- call show !88,0 add函数里对global的赋值无效
- end
- function add(x,y) result(z)
- use mymodule, only:mytype
- type(mytype):: x,y,z
- z%a = x%a + y%b
- z%b = x%b + y%b
- globle = 13 !允许,但无效
- end
需要使用模块时使用
use 模块名的方式。如果只需要使用模块的一部分,可以用“
use 模块名,only: 需要使用的部分”的方式,如上面的add函数。
基于上例,这里展示一下Fortran中重载操作符的方法,
.word.类型的也是一种操作符,类似于.and.和.or.:
- module mymodule
- type mytype
- integer a
- real b
- end type
- interface operator(+) !重载+
- module procedure add
- end interface
- interface operator(.add.) !重载.add.
- module procedure add
- end interface
- contains
- function add(x,y) result(z)
- type(mytype), intent(in):: x,y
- type(mytype) z
- z%a = x%a + y%b
- z%b = x%b + y%b
- end function
- end module
- program interface_test
- use mymodule
- type(mytype) :: t1 = mytype(1,1.)
- type(mytype) :: t2 = mytype(2,2.)
- type(mytype) :: t3
- t3 = add(t1, t2)
- t3 = t1 + t2
- t3 = t1 .add. t2
- print *, t3%a, t3%b !3,3.0
- end
另外要重载赋值操作符的话需要用
interface assignment(=)的方式。
阅读(3973) | 评论(0) | 转发(0) |