4

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

例如,考虑以下程序

program mem
  implicit none

  interface
     function get_matrix() result(res)
       real(kind=kind(0.d0)), allocatable :: res(:,:)
     end function get_matrix
  end interface

  real(kind=kind(0.d0)), allocatable :: w(:,:)

  allocate(w(2,2)) ! Is this necessary?
  w=get_matrix()
  deallocate(w)

end program mem

function get_matrix() result(res)
  real(kind=kind(0.d0)), allocatable :: res(:,:)

  allocate(res(2,2))
  res = 0.d0
  res(1, 1) = 1.d0
  res(2, 2) = 1.d0
end function get_matrix

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

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

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

mem.f90:13:0:

   13 |   w=get_matrix()
      | 
Warning: ‘w.offset’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].lbound’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].ubound’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].lbound’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].ubound’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0:

   13 |   w=get_matrix()
      | 
Warning: ‘w.dim[0].lbound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].ubound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].lbound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].ubound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].ubound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].lbound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].ubound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].lbound’ may be used uninitialized in this function [-Wmaybe-uninitialized]

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

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

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

4

3 回答 3

3

无需预先分配或解除分配。编译器会处理这个问题。以下代码在 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

输出:

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

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

于 2019-11-07T14:19:26.197 回答
2

GCC bugzilla 中有许多类似的错误,这些错误被认为是您的编译器问题,而不是您的代码问题。有些已经修复,有些不是67679 66459 88455和更多(许多重复)。

尝试最新版本的编译器,但它可能仍然存在。

作为一种解决方法,我使用-Wno-maybe-uninitialized.

于 2019-11-07T14:07:44.780 回答
1

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

var = function_ref()

其中右侧是对具有可分配结果的函数的引用,就可分配性质而言,这实际上并没有什么特别之处。该表达式不是可分配的实体。

所以,我们的任务和其他任务一样

var = expr

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

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


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

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

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

var = function_ref() + 1

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

还要考虑这个相关的问题

于 2019-11-07T17:21:06.313 回答