Fortran中不能定义全局变量,但是可以通过一些方式在主程序和函数之间实现变量的共享,这个可以通过两种方式完成,模块module和公共区common。

module

模块的结构如下:

1
2
3
4
5
module 模块名
模块中的变量
contains
模块中的函数
end module 模块名

模块其实就是包含一些变量和函数的一个集合。只要模块中的变量都有save属性,则可以通过module实现数据的共享。gfortran中module里的变量默认就是save的。编译完含module的源代码之后,目录下会出现以module为名字的.mod文件,运行编译完成的二进制文件的时候,这些mod文件是不能删掉的。

例如我想共享常数pi的值和add函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1    module const
2 implicit none
3 real, parameter :: pi=3.1415927
4 contains
5 real function add(x, y)
6 implicit none
7 real :: x, y
8 add = x + y
9 end function add
10 end module const
11 program main
12 use const
13 implicit none
14 write(*,*) add(1.0,2.0)*pi
15 end program main

上述const模块中包含了一个pi变量和一个add函数。use这个模块后方可访问module中的元素,注意这里use需要放在定义变量之前。

module中的元素也有公有和私有之分,类似C++中”class”里的内容(Fortran中也有class关键字,但是完全不是一个东西,C++中class和struct对应Fortran中的type关键字)。公有的变量在模块内或者use这个模块的程序块中都可以访问。私有的变量只有在模块内部进行访问。例如一个根据半径求面积和周长的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1    module circle
2 implicit none
3 private
4 public :: area, length
5 real, parameter :: pi=3.1415927
6 contains
7 real function area(r)
8 implicit none
9 real :: r
10 area = pi*r**2
11 end function area
12 real function length
13 implicit none
14 real :: r
15 length = 2*pi*r
16 end function length
17 end module circle
18 program main
19 use circle, only: area
20 implicit none
21 write(*,*) area(2.0)
22 end program main

若要实现module中成员的公有化或私有化,可以在定义变量之前加上public或private。则该模块内所有成员都会默认公有或者私有。例如上述代码中所加private关键字就会导致pi变量在program中无法访问。在公有环境中若要定义私有成员,则另行声明即可。例如area和length函数的公有化声明。若在私有环境中定义公有,则也是类似方式完成。use一个module的时候,若仅想访问其中的一些成员的时候,可以用only关键字列出期望访问的成员即可。注意这里的function不需要另行声明external。

关于module的内容有很多,module是Fortran90标准之后的一个精髓,熟悉之后会减少很多不必要的工作,其他用法以后还会提及。

common

common充斥在老代码中,用于共享变量,它是通过内存对齐的方式访问的,例如共享数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1    program main
2 implicit none
3 real :: vec1(5), vec2(5)
4 common vec1, vec2
5 call init()
6 write(*,*) vec1, vec2
7 end program main
8 subroutine init()
9 implicit none
10 real :: vec1(5), vec2(5)
11 common vec1, vec2
12 vec1 = 1
13 vec2 = 2
14 end subroutine init

在子程序init中要对变量vec1和vec2进行赋值,则在变量定义完之后利用common关键字进行共享声明即可,但是这里有个问题就是当变量多的时候,若元素没有一一对应,编译的时候这里并不会报错,这样的问题很难查找出来。而且若你仅想共享vec2变量,这里也需要把vec1变量写出来,使得引用变得非常麻烦。module中就不存在这样的问题。另外common的变量还可以分组和利用block data进行赋初值。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1    block data init
2 implicit none
3 real :: r1, r2
4 integer :: i1, i2
5 common /intt/ i1, i2
6 common /reall/ r1, r2
7 data i1,i2,r1,r2 /1,2,1.0,2.0/
8 end block data init
9 program main
10 implicit none
11 real :: r1, r2
12 integer :: i1, i2
13 common /intt/ i1, i2
14 common /reall/ r1, r2
15 write(*,*) i1,i2,r1,r2
16 end program main

在block data中只能使用data进行赋值。这里强烈建议放弃使用common相关语法。当代码量比较大的时候,很容易出现一些很难调试的运行时错误。