1.4. 数组

数组是 Fortran 的**一等公民**,拥有极其强大的数组操作语法。

1.4.1. 数组声明

1.4.1.1. 静态数组

! 一维数组
integer  :: arr1(10)           ! 10 个整数, 索引 1..10
real(8)  :: arr2(0:9)          ! 索引 0..9(显式指定下界)
real     :: arr3(-5:5)         ! 索引 -5..5

! 二维数组
integer  :: mat1(3, 4)         ! 3 行 4 列
real(8)  :: mat2(0:2, 0:3)    ! 指定索引范围

! 三维数组
real :: cube(3, 3, 3)

! 使用参数常量
integer, parameter :: N = 100, M = 200
real(8) :: data(N, M)

备注

Fortran 数组默认索引从 1 开始,与 C 语言不同! 使用 0:9 语法可改为 0 基索引。

1.4.1.2. 可分配数组(动态数组)

! 声明可分配数组
integer, allocatable :: arr(:)
real(8), allocatable :: mat(:, :)

! 分配空间
allocate(arr(100))
allocate(mat(50, 100))

! 使用后释放
deallocate(arr)
deallocate(mat)

! 检查分配状态
if (allocated(arr)) then
    write(*, *) "数组已分配,大小:", size(arr)
end if

小技巧

可分配数组会在过程结束时**自动释放**(Fortran 2003+), 但显式调用 deallocate 仍是好习惯。

1.4.1.3. 自动数组

subroutine process(n)
    integer, intent(in) :: n
    ! 自动数组:在栈上分配,过程结束后自动释放
    real :: local_arr(n)
    ...
end subroutine

1.4.2. 数组初始化

! 直接赋值
integer :: a(5) = [1, 2, 3, 4, 5]

! 数组构造器(Fortran 2003+)
integer :: b(5) = (/ 1, 2, 3, 4, 5 /)   ! 旧式
integer :: c(5) = [ 1, 2, 3, 4, 5 ]     ! 新式(推荐)

! 等间距数组
real :: d(5) = [ (i * 2.0, i = 1, 5) ]

! 全部初始化为相同值
real :: e(100) = 0.0

! 二维数组初始化
integer :: f(2, 3) = reshape([1, 2, 3, 4, 5, 6], [2, 3])
! f(1,1)=1  f(1,2)=3  f(1,3)=5
! f(2,1)=2  f(2,2)=4  f(2,3)=6

重要

Fortran 数组按**列优先**顺序存储: f(1,1) → f(2,1) → f(1,2) → f(2,2) → ...

这恰好与 C 语言的行优先相反。在调用 BLAS/LAPACK 等库时需特别注意。

1.4.3. 数组操作

1.4.3.1. 元素访问

integer :: a(10), mat(3, 4)

! 单个元素
a(1) = 10
mat(2, 3) = 42

! 数组切片(section)
a(1:5) = 1               ! a(1)..a(5) 全部设为 1
a(2:8:2) = 0             ! a(2), a(4), a(6), a(8) 设为 0
a(:) = 0                 ! 全部清零

mat(1, :) = [1, 2, 3, 4]     ! 第一行
mat(:, 2) = [5, 6, 7]        ! 第二列
mat(1:2, 1:2) = 0             ! 子矩阵

1.4.3.2. 整体运算

real :: a(10), b(10), c(10)

a = 1.0                ! 标量广播
b = [ (i * 2.0, i = 1, 10) ]

! 逐元素运算
c = a + b              ! 加法
c = a - b              ! 减法
c = a * b              ! 逐元素乘法(不是矩阵乘法!)
c = a / b              ! 逐元素除法
c = a ** 2             ! 逐元素幂
c = sin(a)             ! 逐元素三角运算
c = sqrt(abs(a))       ! 逐元素组合运算

! 条件赋值
where (a > 0.5) b = a * 2.0

小心

a * b 是**逐元素乘法**(Hadamard 积), 矩阵乘法需使用 matmul(a, b) 函数。

1.4.4. 内置数组函数

real :: a(100), mat(10, 10)

! 形状查询
size(a)                  ! 100(元素总数)
size(mat, dim=1)         ! 10(第一维大小)
shape(mat)               ! [10, 10](形状数组)
rank(mat)                ! 2(维数)
allocated(a)             ! .true./.false.

! 边界查询
lbound(a)                ! 1(下界)
ubound(a)                ! 100(上界)

! 归约
sum(a)                   ! 求和
product(a)               ! 求积
maxval(a)                ! 最大值
minval(a)                ! 最小值
maxloc(a)                ! 最大值位置
minloc(a)                ! 最小值位置

! 矩阵运算
matmul(m1, m2)           ! 矩阵乘法
transpose(mat)           ! 转置
dot_product(v1, v2)      ! 向量点积

! 重排
pack(arr, mask)          ! 按掩码选取
unpack(...)              ! 反向操作
reshape(arr, shape)      ! 改变形状
cshift(arr, shift)       ! 循环位移
eoshift(arr, shift)      ! 边界位移

1.4.5. 数组作为参数

! 假定形状数组(常用推荐方式)
subroutine process(arr)
    integer, intent(inout) :: arr(:)     ! 一维假定形状
    ! arr 的索引从 1 开始(除非调用时指定下界)
    write(*, *) "大小:", size(arr)
end subroutine

! 显式形状数组
subroutine fixed_arr(arr)
    integer, intent(in) :: arr(10, 20)   ! 固定大小
end subroutine

! 假定大小数组(旧式,不推荐)
subroutine old_style(arr)
    integer :: arr(*)                     ! 不安全
end subroutine

小技巧

假定形状数组 arr(:) 是最佳实践: - 自动传递数组形状信息 - 利用 size、lbound 等查询 - 不限制调用方的数组大小

1.4.5.1. 可分配数组作为过程参数

subroutine create_array(arr, n)
    integer, allocatable, intent(out) :: arr(:)
    integer, intent(in) :: n

    allocate(arr(n))
    arr = [ (i, i = 1, n) ]
end subroutine

program main
    implicit none
    integer, allocatable :: arr(:)

    call create_array(arr, 10)
    write(*, *) arr       ! [1, 2, 3, ..., 10]
    deallocate(arr)
end program main