【问题标题】:Fortran function returning unallocated array causes segmentation faultFortran 函数返回未分配的数组导致分段错误
【发布时间】:2018-06-17 17:03:55
【问题描述】:

我正在为一些 MPI 分散/收集例程使用一些现代 Fortran 包装器。我试图有一个包装器接口,它在输入上只有一个数组,并在输出上返回 MPI 操作的结果,对于几个派生类型,做这样的事情:

type(mytype), allocatable :: chunk(:),whole(:)

! [...] chunk descends from previous parts of the code

! Get global array
whole = gatherv(array=chunk,receiver_node=cpuid)

我正在使用返回 allocatable 数组的函数来执行此操作。但是,每当我返回未分配的结果时,gcc 6.2.0gcc 7.1.0 都会出现分段错误。

我需要未分配结果的原因是有时我只需要在指定的 CPU 上收集整个数组,所以我不想在所有其他节点上浪费内存:接收节点返回一个分配的数组数据和所有其他节点接收到一个空且已释放的数组。

这是重现问题的示例代码:

program test_allocatable_fun
    implicit none

    integer, allocatable :: my_array(:)
    integer :: n

    n = 3; my_array = unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
    n =-3; my_array = unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
    n = 5; my_array = unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
    n = 0; my_array = unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)

    return


    contains

    function unallocated_array(n) result(array)
        integer, intent(in) :: n
        integer, allocatable :: array(:)
        integer :: j
        if (n>0) then
            allocate(array(n))
            array(:) = [(j,j=1,n)]
        else
            if (allocated(array)) deallocate(array)
        end if
    end function unallocated_array

end program test_allocatable_fun

分段错误发生在赋值行,即:

my_array = unallocated_array(n)

你们之前有没有遇到过同样的问题?或者,我是否违反了标准中的任何内容?我不明白为什么应该强制返回可分配数组的函数分配返回值。是不是和在子程序中有一个intent(out) 虚拟变量一样?

【问题讨论】:

  • 虽然我对此有更多思考,但为什么不将数组分配给 0 size 呢?我怀疑这可能是正确的方法。这样你的代码就不需要一直检查返回数组的分配状态。
  • 感谢@HighPerformanceMark 的评论 - 分配给 0 大小的数组可以解决问题。除了能够使用 size() 内在函数之外,内存中未分配和 0 大小的已分配数组之间有什么区别?
  • 对于未分配的数组(或标量),除了测试其分配状态外,您几乎无能为力。正如您所发现的,如果您不确定是否分配了一个数组,那么每次您触摸它时,您都必须检查它的状态并处理不同代码路径上的两种返回可能性。分配到大小为 0 的数组几乎可以像任何其他已分配的数组一样处理。

标签: function fortran gfortran fortran2003 allocatable-array


【解决方案1】:

函数结果与具有intent(out) 属性的虚拟参数不同。此处的显着不同之处在于,必须始终在函数执行终止时定义非指针函数结果。 Fortran 2008 12.6.2.2 p4 涵盖了这一点。

对于一个可分配的函数结果(任何对象)被定义是必要的,但不是充分的。

在某种程度上,您可以将其视为始终引用函数结果的方式(否则不会执行该函数)。未定义的实际参数也可能不会被引用,但这种引用不会是“自动的”。

如 cmets 中所述,函数结果可以分配为大小为零的数组。零大小的数组总是具有定义的值。

您可以在this other question 中看到零大小数组和未分配数组的一些比较。

【讨论】:

    【解决方案2】:

    我怀疑主程序中的任何可分配数组都包含全局数据。我通常将这些变量放在一个模块中。这样,这个变量就不需要被传递,并且可以在主程序、子程序和/或函数中分配和释放。

    由于数组是唯一的返回值,我把它改成了一个子程序。这对你有什么作用?附言'implicit none' 应该出现在每个模块、程序、函数和子程序中。

    module fun
       implicit none
       integer, allocatable :: my_array(:)
    end module fun
    
    program test_allocatable_fun
       use fun
       implicit none
    
       integer :: n
    
       n = 3; call unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
       n =-3; call unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
       n = 5; call unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
       n = 0; call unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
    
       return
    
       contains
    
    subroutine unallocated_array(n)
    
        use fun
        implicit none
    
        integer, intent(in) :: n
        integer :: j
        if (n>0) then
            allocate(my_array(n))
            my_array(:) = [(j,j=1,n)]
        else
            if (allocated(my_array)) deallocate(my_array)
        end if
    end subroutine unallocated_array
    
    end program test_allocatable_fun
    

    【讨论】:

    • 这并不能真正回答问题。您不能对所有内容都使用全局变量。许多人认为全局变量是一个非常糟糕的设计决策,应该尽可能避免。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-09
    • 1970-01-01
    • 1970-01-01
    • 2015-06-20
    • 1970-01-01
    相关资源
    最近更新 更多