You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含指针的Fortran类型实例的正确分配方式

Fortran含指针成员类型的安全初始化方式

示例代码

module lib
    private
    type node
        integer::val
        type(node),allocatable::next
    end type
    type,public::list
        integer::num=0
        ! `first` should be a pointer to avoid making `this` a target in procedures.
        type(node),pointer::first=>null(),last=>null()
    contains
        procedure::add=>list_add
        final::list_final
    end type
    interface list
        procedure list_new
    end interface
contains
    function list_new()result(this)
        type(list)::this
        print*,'list_new'
        allocate(this%first)
        this%last=>this%first
    end
    subroutine list_add(this,val)
        class(list),intent(inout)::this
        integer,intent(in)::val
        print*,'list_add'
        this%num=this%num+1
        allocate(this%last%next)
        this%last=>this%last%next
        this%last%val=val
    end
    subroutine list_final(this)
        type(list),intent(inout)::this
        print*,'list_final'
        ! Checking if `first` is associated is necessary to avoid the error on
        ! finalization of uninitialized instances.
        if(associated(this%first))deallocate(this%first)
    end
end


program prog
    use lib
    type(list)::obj  ! type(list),allocatable::obj
    obj=list()  ! allocate(obj,source=list())
    call obj%add(42)
end

问题现象

这是一段用可分配节点实现链表的Fortran代码,first被设为指针并在构造函数list_new中分配内存,list_final终结过程负责自动释放内存避免泄漏。但使用gfortran-13.1.0加-g选项编译运行时,会在allocate(this%last%next)行触发运行时错误:

Fortran runtime error: Attempting to allocate already allocated variable 'this'

错误发生前的输出为:

list_new
 list_final
 list_final
 list_add

原因分析

list_final被调用了两次:

  1. 第一次终结声明type(list)::obj时创建的默认初始化实例;
  2. 第二次终结obj=list()右侧生成的临时实例——临时实例的内容复制给obj后,其终结过程释放了自身first指向的内存,但obj%first已指向同一块内存,导致obj的指针变为悬空状态,后续调用list_add时触发错误。

解决方案

将type(list)::obj替换为type(list),allocatable::obj,同时把obj=list()替换为allocate(obj,source=list()),即可解决问题,且此时list_final不会被调用。

核心问题解答

带source的allocate并非此类类型实例初始化的唯一正确方式,但确实是无需重新定义赋值操作、无需用显式析构替代final的前提下,最安全简洁的方式之一。其他可行方案包括:

  • 自定义赋值运算符:在赋值时实现指针的深拷贝,避免原实例与目标实例共享内存。但这种方式需要编写额外的赋值过程代码,维护成本较高。
  • 调整类型成员设计:将first从指针改为可分配成员,利用Fortran默认赋值的深拷贝特性自动管理内存,无需手动处理指针生命周期。但这需要适配原代码的设计需求。

而obj=list()这种写法的问题根源在于,Fortran默认赋值对指针成员执行浅拷贝——仅复制指针地址而非指向的内存块。当临时实例被终结释放内存后,目标实例的指针就会变为悬空状态,进而触发运行时错误。这种写法虽看似简洁,但对于含指针成员且带有终结过程的类型,存在明确的安全风险。

内容的提问来源于stack exchange,提问作者V T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 13:04:53