1.8. 派生类型与指针
Fortran 90+ 引入了派生类型(类似 C 的 struct)和指针(类似 C 的指针但更安全)。
1.8.1. 派生类型(Derived Type)
1.8.1.1. 定义与使用
! 定义派生类型
type :: Person
character(50) :: name
integer :: age
real :: height ! 身高(米)
logical :: employed
end type Person
! 声明变量
type(Person) :: alice, bob
! 成员赋值
alice%name = "Alice"
alice%age = 30
alice%height = 1.68
alice%employed = .true.
! 结构体构造器
bob = Person("Bob", 25, 1.75, .false.)
! 整体赋值
alice = bob ! 复制所有成员
备注
使用 % 访问成员(不是 C 的 . 或 ->)。
1.8.1.2. 嵌套派生类型
type :: Date
integer :: year
integer :: month
integer :: day
end type
type :: Employee
integer :: id
character(50) :: name
type(Date) :: hire_date ! 嵌套
type(Date) :: birth_date
logical :: active
end type
! 嵌套初始化
type(Employee) :: emp
emp = Employee(id=2001, &
name="Eve", &
hire_date=Date(2020, 3, 15), &
birth_date=Date(1990, 7, 22), &
active=.true.)
! 访问嵌套成员
write(*, *) "入职年份:", emp%hire_date%year
1.8.1.3. 派生类型数组
type(Person) :: people(100)
type(Person), allocatable :: team(:)
! 初始化
people(1) = Person("Alice", 30, 1.68, .true.)
people(2) = Person("Bob", 25, 1.75, .false.)
! 分配动态数组
allocate(team(50))
team(1) = Person("Charlie", 35, 1.80, .true.)
! 数组遍历
integer :: i
do i = 1, 2
write(*, *) trim(people(i)%name), people(i)%age
end do
1.8.1.4. 派生类型过程绑定(Fortran 2003+ OOP)
type :: Vector
real :: x, y, z
contains
procedure :: length => vector_length
procedure :: normalize => vector_normalize
end type
contains
real function vector_length(this)
class(Vector), intent(in) :: this
vector_length = sqrt(this%x**2 + this%y**2 + this%z**2)
end function
subroutine vector_normalize(this)
class(Vector), intent(inout) :: this
real :: len
len = this%length()
if (len > 0) then
this%x = this%x / len
this%y = this%y / len
this%z = this%z / len
end if
end subroutine
! 使用
type(Vector) :: v
v = Vector(1.0, 2.0, 3.0)
write(*, *) v%length() ! 通过 % 调用绑定过程
call v%normalize()
1.8.1.5. 可分配组件
type :: DynamicData
integer :: n
real, allocatable :: values(:) ! 可分配数组组件
end type
type(DynamicData) :: d
d%n = 100
allocate(d%values(d%n))
d%values = [ (i * 1.0, i = 1, d%n) ]
! 可在声明时自动分配(Fortran 2003+)
type(DynamicData) :: d2 = DynamicData(50, [ (i*1.0, i=1,50) ])
! 自动释放
deallocate(d%values)
1.8.2. Fortran 指针
Fortran 指针是**类型安全**的别名,不同于 C 指针(不是地址运算)。
1.8.2.1. 基本用法
integer, target :: x = 42 ! target: 可被指针指向
integer, pointer :: p ! 指针声明
p => x ! 指针指向 x(不是地址运算)
write(*, *) p ! 42
p = 100 ! 通过指针修改
write(*, *) x ! 100(x 被修改!)
重要
p => x 是**绑定**(pointer association),不是取地址!
Fortran 指针永远指向一个有 target 属性的变量。
1.8.2.2. 指针与数组
integer, target :: arr(10)
integer, pointer :: p(:)
! 指向整个数组
p => arr
p = [ (i, i = 1, 10) ]
! 指向数组切片
p => arr(1:5)
p => arr(1:10:2) ! 步长切片 [1,3,5,7,9]
! 动态分配指针(不依赖 target)
allocate(p(100))
p = 0
deallocate(p)
1.8.2.3. 可分配 vs 指针
特性 |
|
|
|---|---|---|
内存管理 |
自动自动释放 |
需手动 |
别名能力 |
不能 |
可以指向 target |
数组切片指向 |
不能 |
可以 |
性能 |
更好(编译器优化) |
有额外开销 |
推荐度 |
优先使用 |
仅在需要别名时使用 |
小技巧
在 Fortran 中,能用 allocatable 的地方**不要用 pointer**——
可分配数组更安全、更高效。
1.8.3. 链表示例
简单的链表实现
module linked_list
implicit none
private
type, public :: node
integer :: value
type(node), pointer :: next => null()
end type
contains
subroutine append(head, val)
type(node), pointer, intent(inout) :: head
integer, intent(in) :: val
type(node), pointer :: curr
if (associated(head)) then
curr => head
do while (associated(curr%next))
curr => curr%next
end do
allocate(curr%next)
curr%next%value = val
curr%next%next => null()
else
allocate(head)
head%value = val
head%next => null()
end if
end subroutine
subroutine print_list(head)
type(node), pointer, intent(in) :: head
type(node), pointer :: curr
curr => head
do while (associated(curr))
write(*, '(I0, " -> ")', advance="no") curr%value
curr => curr%next
end do
write(*, *) "null"
end subroutine
end module
小心
Fortran 的指针操作比 C 更受限:
- 无地址运算(ptr + 1 不允许)
- 无指针类型转换
- associated() 检查指针是否被绑定
- 指针只能重定向(不能修改地址关系外的数据)