首页 >

FAQ 之 common,Module共享数据的优缺点

作者:fcode  日期:01-23
来源:Fcode研讨团队
在较老的 FORTRAN 代码中,经常看到 common 语句。(它的官方名字叫 公共区,而不叫全局变量)

然而,这个历史上的语句却具有非常多的缺陷:
1. 它只说明变量具有在公共区的属性,并不说明变量的类型和精度。
2. 公共区采用内存字节一一对应,而不是采用变量名称一一对应。这极易发生错误,且难以排查和调试。

因此,我们强烈建议,在新书写的代码里,避免使用 COMMON 语句,而使用 Module 语句代替它。

一. COMMON 的弊病

下面我们来看一个例子,如下代码:
program www_fcode_cn
  Implicit None
  common a , b  
  Integer :: a = 1 , b = 2 !// common 并不说明类型,需单独定义类型
  call SubOK() 
  call SubOrder()
  call SubType()
  call SubKind()
End Program www_fcode_cn

Subroutine SubOK()
  Implicit None
  common a , b
  Integer :: a , b
  write(*,*) a , b !// 输出1,2,正确
End Subroutine SubOK

Subroutine SubOrder()
  Implicit None
  common b , a !// common 按顺序对应
  Integer :: a , b
  write(*,*) a , b !// 输出 2,1 颠倒
End Subroutine SubOrder

Subroutine SubType()
  Implicit None
  common a , b
  real :: a , b !// 类型不一致
  write(*,*) a , b 
  !// 输出 1.40129846E-45   2.80259693E-45 完全错误
End Subroutine SubType

Subroutine SubKind()
  Implicit None
  common a , b
  Integer(Kind=8) :: a , b !// Kind 不一致
  write(*,*) a , b 
  !// 输出 8589934593  0 完全错误
End Subroutine SubKind
我们看到,主程序中书写了 common 语句,其中有两个变量 a 和 b。但由于 common 并不说明变量的类型和精度,因此我们需要单独定义它们,使用语句: integer a , b

如果没有显式定义,也没有 Implicit None 语句,那么按照 IN 规则,以 a b 开头的变量,为 real 类型(我们同样强烈建议您书写 Implicit None,而不要省略它)

我们分别在4个子程序中使用了 common 公共区的数据。但只有 SubOK 中是正确的,它必须具有相同的变量顺序,变量类型,变量精度(Kind值)。

在 SubOrder 中,a 和 b 的顺序写反了。这就导致值也不正确了。

而在 SubType 中,a 和 b 被定义成了 real 类型。这样的结果更糟糕,完全面目全非。

即便是 Integer 类型,Kind值不同,也会影响 common 的正确对应。如 SubKind 函数中的演示。

同时,common 公共区的变量,需要在每一个使用它的函数内重新定义,且必须保证对应变量的顺序、精度、大小、长度等完全一致。稍有差别就会出现对应“错位”

因此,在实际工作中,common 的使用,加上没有 Implicit None 而出现的 IN 规则,若干变量未定义,或变量的精度不确定。就非常容易发生错误。

二. Module 共享数据的方便、可靠

Module 是 Fortran90 引入的一个重要概念。关于它的内容比较多,在此仅简单介绍它的一小部分内容(用于数据的共享)。

一个 Module 是一个代码单元,与主程序,外部子(例行)程序或函数是差不多的“等级”。但它的功能更强大,也更便捷。

在 Module 中,我们可以定义若干变量(含数组、type、可分配数组等) 。这些变量会存储在一个特定的空间内,而被所有 use 了该模块的程序单元直接使用。而且这种使用,是按照变量的名称而对应的,而非变量的内存字节顺序。

同时,use 了该模块的程序单元,也继承了这些变量的类型、精度、大小、长度等属性,而不必(也不能)重新定义。

Module modname
  Implicit None
  !// 明确变量类型,顺序无关
  Integer , save :: a = 1 , b = 2  
End Module modname

program www_fcode_cn
  use modname !// 无需,也不能再定义 a b
  Implicit None 
  write(*,*) b , a !// 输出 2,1 正确
  a = 3
  b = 4
  !// 按变量名对应,因此倒序输出,其值也倒序
  call Sub()
End Program www_fcode_cn

Subroutine Sub()
  use modname !// 无需,也不能再定义 a b
  Implicit None
  write(*,*) a , b !// 输出 3,4 正确
End Subroutine Sub
在这段代码中,我们书写了一个 Module,名叫 modname。其中包含两个变量 a b,并含有初值 1 和 2

save 属性表明这些变量会被保存起来,以便在不同的程序单元间保持同样的值。
(虽然语法未明确指出,但所有的编译器都默许 Module 中的变量具有 save 属性,因此,很多时候 save 也可以忽略不写)

在主程序和子程序 Sub 中,我们的 use modname 语句,表示这两个程序单元都使用了 modname 这个模块。

由于使用了这个模块,在主程序和 sub 中,可以直接访问 a b 两个变量,而无需(也不能)再次声明他们。

在主程序里,我们的 a b 输出顺序颠倒了。但可以看到,输出的结果是符合我们预期的。

同时,在主程序中改变 a b 的值,同样会影响 sub 子程序中对应的 a b 的值。实现了数据的共享。

由于 Module 内变量的方便使用、共享,同时根据名称来对应也不容易出错。无需再次声明也不会出现精度变量的不一致。综合这些优点,Module 可完全替代,更应该替代 COMMON。

除此之外,Module 还可以封装若干子程序(或函数),对变量设置私有(private)或保护(Protect)属性,对具有特殊用途的变量进行权限的分级。

如果读者有兴趣,可以从较新的教材(书名里的 Fortran 为小写字母)里找到更多的 Module 的介绍,您会发现,Module 是一个广阔神奇的新世界。
常规|工具|专业|读物|
代码|教学|算法|
首页 >
FortranCoder手机版-导航