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