Fortran 函数返回可分配数组

fra*_*sco 7 fortran function allocatable-array

让我考虑一个返回一个的函数allocatable数组的函数。是否应该在赋值之前分配保存结果的数组变量(在函数外部)?

\n

例如,考虑以下程序

\n
program mem\n  implicit none\n\n  interface\n     function get_matrix() result(res)\n       real(kind=kind(0.d0)), allocatable :: res(:,:)\n     end function get_matrix\n  end interface\n  \n  real(kind=kind(0.d0)), allocatable :: w(:,:)\n\n  allocate(w(2,2)) ! Is this necessary?\n  w=get_matrix()\n  deallocate(w)\n\nend program mem\n\nfunction get_matrix() result(res)\n  real(kind=kind(0.d0)), allocatable :: res(:,:)\n\n  allocate(res(2,2))\n  res = 0.d0\n  res(1, 1) = 1.d0\n  res(2, 2) = 1.d0\nend function get_matrix\n
Run Code Online (Sandbox Code Playgroud)\n

根据thisthisres ,为 的 结果分配的数组get_matrix一旦超出范围就会被释放。

\n

w对主程序中未分配变量的赋值是否会延长结果的范围get_matrix?换句话说,如果我allocate(w(2,2))在主程序中省略,我会得到未定义的行为吗?

\n

省略allocate(w(2,2))并使用gfortran 9.2.0和 选项进行编译-Wall -std=f2008会出现以下警告

\n
mem.f90:13:0:\n\n   13 |   w=get_matrix()\n      | \nWarning: \xe2\x80\x98w.offset\xe2\x80\x99 is used uninitialized in this function [-Wuninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[0].lbound\xe2\x80\x99 is used uninitialized in this function [-Wuninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[0].ubound\xe2\x80\x99 is used uninitialized in this function [-Wuninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[1].lbound\xe2\x80\x99 is used uninitialized in this function [-Wuninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[1].ubound\xe2\x80\x99 is used uninitialized in this function [-Wuninitialized]\nmem.f90:13:0:\n\n   13 |   w=get_matrix()\n      | \nWarning: \xe2\x80\x98w.dim[0].lbound\xe2\x80\x99 may be used uninitialized in this function [-Wmaybe-uninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[0].ubound\xe2\x80\x99 may be used uninitialized in this function [-Wmaybe-uninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[1].lbound\xe2\x80\x99 may be used uninitialized in this function [-Wmaybe-uninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[1].ubound\xe2\x80\x99 may be used uninitialized in this function [-Wmaybe-uninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[0].ubound\xe2\x80\x99 may be used uninitialized in this function [-Wmaybe-uninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[0].lbound\xe2\x80\x99 may be used uninitialized in this function [-Wmaybe-uninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[1].ubound\xe2\x80\x99 may be used uninitialized in this function [-Wmaybe-uninitialized]\nmem.f90:13:0: Warning: \xe2\x80\x98w.dim[1].lbound\xe2\x80\x99 may be used uninitialized in this function [-Wmaybe-uninitialized]\n
Run Code Online (Sandbox Code Playgroud)\n

但是,使用 运行程序valgrind以及使用-fbounds-check-fsanitize=address或进行编译-fsanitize=leak不会给出任何错误。deallocate(w)此外,最后的指令不会使程序崩溃,表明w包含由分配的内存get_matrix,因此不需要w在主程序中分配。

\n

同时,包含allocate(w(2,2))在代码中会抑制编译器警告。尽管给人的印象是相同的内存被分配了两次,valgrind并没有报告内存泄漏,而且实际上报告了完全相同的内存使用情况。

\n

allocatable将数组存储为函数结果的正确方法是什么?\nw在将 的结果存储在数组中之前是否需要进行分配get_matrix

\n

ja7*_*a72 5

无需预分配或取消分配。编译器会处理这个问题。以下代码在 Intel fortran 中按预期工作。

program Console1
implicit none
! Variables
real(8), allocatable :: A(:,:)
integer :: i, j

! Body of Console1

A = get_matrix(6,4)

do i=1, size(A,1)
    print '(*(g9.4),1x)', A(i,:)
end do    

contains

function get_matrix(n,m) result(res)
integer, intent(in) :: n,m
real(8), allocatable :: res(:,:)
integer :: i

    allocate(res(n,m))
    res = 0d0
    forall(i=1:min(n,m)) res(i,i)=1d0

end function

end program Console1
Run Code Online (Sandbox Code Playgroud)

输出:

1.000    0.000    0.000    0.000
0.000    1.000    0.000    0.000
0.000    0.000    1.000    0.000
0.000    0.000    0.000    1.000
0.000    0.000    0.000    0.000
0.000    0.000    0.000    0.000
Run Code Online (Sandbox Code Playgroud)

附言。使用关键字将函数放入程序声明中contains。这样它们就不是外部函数,也不需要接口声明。


fra*_*lus 5

ja72 和 Vladimir F 的答案是正确的。然而,为了完整起见,我将讨论另一点。在声明中

var = function_ref()
Run Code Online (Sandbox Code Playgroud)

其中右侧是对具有可分配结果的函数的引用,实际上在可分配性质方面没有什么特别的。该表达式不是可分配的实体。

所以,我们的任务很像任何其他任务

var = expr
Run Code Online (Sandbox Code Playgroud)

右侧是一个表达式。也就是说,从具有可分配结果的函数进行分配不需要特殊考虑。(当然,函数结果必须被分配,但这是不同的一点。)

在该问题的情况下,适用通常的内在赋值规则。特别是,我们不需要w在赋值之前进行分配。


deallocate(w)此外,最后的指令不会使程序崩溃,表明w包含由分配的内存get_matrix,因此不需要w在主程序中分配。

还有其他的话要说。的函数结果get_matrix在赋值中使用后本身会被释放。 w是与函数结果分开的实体,并且内部赋值导致 的分配w

因此,不,您不会“延长结果的范围”:您已将其复制到新分配的变量中,然后在完成后将其释放。考虑像这样的表达式

var = function_ref() + 1
Run Code Online (Sandbox Code Playgroud)

同样,我们有一个要分配的表达式,但我们是否期望函数结果“持续”?

还要考虑这个相关问题