模块内部子程序 "contains" 和模块外部子程序的区别

Difference between subroutine inside module "contains" and outside module

我有一个很大的源代码,其中模块中的子例程定义在 module ... end module 语句之外(即不在 contains 语句内)。我在下面包含了一个简化的模块:

module core
  implicit none

  type :: disc_status
    sequence

    real*8 :: alpha1, alpha2, alpha3

  end type disc_status
end module core

subroutine tester(input)
  use core
  type(disc_status), intent(in) :: input
  print *, input%alpha1, input%alpha2, input%alpha3
end subroutine tester

这是一个使用模块和子例程的示例程序:

program flyingDiscSimulator

use core
implicit none

type(disc_status) :: disc

disc%alpha1 = 1.1D0
disc%alpha2 = 1.2D0
disc%alpha3 = 1.3D0

call tester(disc)

print *, 'it works'

end program flyingDiscSimulator

通常,我最终会看到子例程在模块中使用 contains 语句:

module core
  implicit none

  type :: disc_status
    sequence

    real*8 :: alpha1, alpha2, alpha3

  end type disc_status

   contains
     subroutine tester(input)
       type(disc_status), intent(in) :: input
       print *, input%alpha1, input%alpha2, input%alpha3
     end subroutine tester

end module core

但是,上面引用的程序文件不需要任何更改即可使用在模块中包含子例程的任何一种方式(无论如何使用 gfortran)。因此,两种解决方案之间的模块或其子例程的使用似乎没有区别。 这两种风格之间是否存在任何“内幕”差异?

模块内的过程将有一个显式接口(也就是编译器知道参数的特征)。 而模块外的过程将有一个隐式接口(也就是编译器必须对参数做出假设)。

显式接口有助于编译器发现编程错误,因此受到青睐。 有关优势的更深入讨论,请参阅 this answer.


我什至不确定在 core 之外定义时在程序内部调用 tester 是否有效?

use core 应该只是让模块的 public 对象已知,测试程序需要它自己的 external tester 行。

版本

module m
contains
  subroutine s()
  end subroutine s
end module m

module m
end module m

subroutine s()
end subroutine s

说完全不同的东西,但在问题的情况下,最终结果大同小异。

这里的第一个版本创建了一个模块过程 s,主机m;第二个版本有一个没有主机的外部程序s

虽然问题的例子有使用模块的外部过程,但更普遍的区别是:模块过程可以访问模块中的 all 个实体(除了当通过 import 语句或被本地名称遮盖而无法访问;使用该模块的外部过程只能访问那些 public 实体。

但是主程序来了效果就不同了。外部子例程及其名称是 global 实体。转到我的第二个版本,然后

program main
  call s
end program

是调用外部子程序s的有效程序。这个子程序引用是有效的,因为主程序中 s 隐式接口 就足够了。如果外部子程序 s 显式接口 更重要,那么这里的主程序是不允许的。在主程序中使用 external s 语句来强化 reader 子例程是外部子例程(具有隐式接口)是可以接受的,但不是必需的。 (implicit none external 需要 external s。)

问题的例子是这样一个显式接口

模块过程在可访问时始终具有可用的显式接口。但是,模块过程不是全局实体,它的名称也不是全局标识符。

在幕后,存在实现差异(源于上述):最值得注意的是,编译器通常会“名称混淆”模块过程。

综上所述,问题的两种做法之间存在差异,但在这种情况下不会被程序员注意到。