1.6. 模块与作用域
模块(module)是 Fortran 90 最重要的现代化特性,用于封装数据、类型和过程。
1.6.1. 模块基础
1.6.1.1. 基本结构
math_utils.f90
module math_utils
implicit none
private ! 默认私有
public :: pi, add, factorial ! 显式公开接口
real, parameter :: pi = 3.14159265
contains
function add(a, b) result(r)
implicit none
integer, intent(in) :: a, b
integer :: r
r = a + b
end function add
recursive function factorial(n) result(r)
implicit none
integer, intent(in) :: n
integer :: r
if (n <= 1) then
r = 1
else
r = n * factorial(n - 1)
end if
end function factorial
! 私有过程:模块内部使用
function internal_helper(x) result(y)
real, intent(in) :: x
real :: y
y = x + 1.0
end function
end module math_utils
重要
private 声明在模块开头,使所有实体默认私有。
使用 public :: name 显式公开需要的接口——这叫**最小暴露原则**。
1.6.1.2. 使用模块
program main
use math_utils ! 引入模块
implicit none
! 可以直接使用模块中的公开实体
write(*, *) pi ! 3.14159265
write(*, *) add(3, 4) ! 7
write(*, *) factorial(5) ! 120
! internal_helper 不可见(private)
! write(*, *) internal_helper(1.0) ! 错误!
end program main
1.6.1.3. 利用 only 精确控制
! 只引入需要的实体——避免命名冲突
use math_utils, only: pi, factorial
! 重命名引入的实体——解决冲突
use math_utils, only: compute_pi => pi
! 引入全部(不推荐——容易意外覆盖)
use math_utils
备注
使用 only 子句是良好的编程习惯:
- 明确代码依赖了什么
- 避免命名空间污染
- 编译器可以更好地优化
1.6.2. 模块的可见性控制
module data_module
implicit none
private ! 全部默认私有
! 公共接口
public :: set_value, get_value, init
! 模块级数据(封装,类似 C 的 static)
integer :: shared_value = 0
contains
subroutine init()
shared_value = 0
end subroutine
subroutine set_value(v)
integer, intent(in) :: v
shared_value = v
end subroutine
function get_value() result(v)
integer :: v
v = shared_value
end function
end module
小技巧
这种封装模式保证了数据只能通过公开接口访问——
这就是 Fortran 版本的"数据隐藏"(类似 C 的 static 或 Java 的 private)。
1.6.3. 嵌套模块(子模块)
Fortran 2008+ 支持子模块,将大型模块拆分为多文件。
! 父模块
module matrix_ops
implicit none
private
public :: matrix_multiply
contains
interface
module subroutine matrix_multiply(a, b, c)
real, intent(in) :: a(:,:), b(:,:)
real, intent(out) :: c(:,:)
end subroutine
end interface
end module
! 子模块
submodule (matrix_ops) matrix_ops_impl
implicit none
contains
module subroutine matrix_multiply(a, b, c)
! 实现放在这里,主模块更简洁
c = matmul(a, b)
end subroutine
end submodule
1.6.4. 模块间依赖
模块间的 use 链
! module_a.f90
module module_a
implicit none
real, parameter :: pi = 3.14
end module
! module_b.f90
module module_b
use module_a ! 依赖 module_a
implicit none
real, parameter :: two_pi = 2 * pi
end module
! main.f90
program main
use module_b ! 间接使用 module_a
implicit none
write(*, *) two_pi
end program main
小心
模块间**不能循环依赖**(A use B, B use A)。 可通过将公共类型提取到单独的"基础模块"来解决。
1.6.5. 作用域规则
program scope_demo
implicit none
integer :: x = 10 ! 程序级变量
call inner()
contains
subroutine inner()
integer :: x = 20 ! 局部变量,遮蔽外部 x
write(*, *) x ! 20
end subroutine
end program
作用域级别 |
可见性范围 |
生命周期 |
|---|---|---|
程序/模块级 |
整个程序/模块 |
程序运行期 |
过程级 |
过程内部 |
过程调用期间 |
代码块级 |
|
代码块执行期间 |
|
模块内(可用 |
程序运行期 |
|
过程内 |
程序运行期(保留值) |
1.6.6. block 结构(Fortran 2008+)
program block_demo
implicit none
integer :: x = 10
block
integer :: x ! 块作用域变量
x = 20
write(*, *) "块内:", x ! 20
end block
write(*, *) "块外:", x ! 10
end program
小技巧
block 结构可以创建临时作用域,避免变量名冲突,
也便于在需要时才声明临时变量。
1.6.7. 模块组织建议
项目模块组织示例:
constants.f90 → 模块: constants(物理常数、参数)
types.f90 → 模块: types(派生类型定义)
io.f90 → 模块: io_module(I/O 操作)
math.f90 → 模块: math_utils(数学函数)
main.f90 → 程序: main(主程序入口)
备注
IVF 2011 中,模块的编译顺序很重要:先编译被依赖的模块。 通常在解决方案管理器中按依赖顺序排列文件。