【问题标题】:Fortran 90 function return pointerFortran 90 函数返回指针
【发布时间】:2014-07-24 03:15:07
【问题描述】:

我看到了这个问题:

Fortran dynamic objects

接受的答案让我怀疑我是否安全地编写了以下函数(不允许内存泄漏)

   function getValues3D(this) result(vals3D)
     implicit none
     type(allBCs),intent(in) :: this
     real(dpn),dimension(:,:,:),pointer :: vals3D
     integer,dimension(3) :: s
     if (this%TF3D) then
       s = shape(this%vals3D)
       if (associated(this%vals3D)) then
            stop "possible memory leak - p was associated"
       endif
       allocate(vals3D(s(1),s(2),s(3)))
       vals3D = this%vals3D
     else; call propertyNotAssigned('vals3D','getValues3D')
     endif
   end function

当我运行我的代码时会出现这个警告,但如果我之前(到这个函数)设置了我的this%vals3D,不应该关联它吗?我目前遇到内存错误,当我引入一个包含此功能的新模块时,它们开始出现。

非常感谢任何帮助。

我认为我不够具体。我想制作以下课程,并知道如何在内存方面安全地实现该课程。那就是:

   module vectorField_mod
   use constants_mod
   implicit none

   type vecField1D
     private
     real(dpn),dimension(:),pointer :: x
     logical :: TFx = .false.
   end type

   contains

   subroutine setX(this,x)
     implicit none
     type(vecField1D),intent(inout) :: this
     real(dpn),dimension(:),target :: x
     allocate(this%x(size(x)))
     this%x = x
     this%TFx = .true.
   end subroutine

   function getX(this) result(res)
     implicit none
     real(dpn),dimension(:),pointer :: res
     type(vecField1D),intent(in) :: this
     nullify(res)
     allocate(res(size(this%x)))
     if (this%TFx) then
       res = this%x
     endif
   end function

   end module

下面的代码测试这个模块的地方

   program testVectorField
   use constants_mod
   use vectorField_mod
   implicit none

   integer,parameter :: Nx = 150
   real(dpn),parameter :: x_0 = 0.0
   real(dpn),parameter :: x_N = 1.0
   real(dpn),parameter :: dx = (x_N - x_0)/dble(Nx-1)
   real(dpn),dimension(Nx) :: x = (/(x_0+dble(i)*dx,i=0,Nx-1)/)
   real(dpn),dimension(Nx) :: f
   real(dpn),dimension(:),pointer :: fp
   type(vecField1D) :: f1
   integer :: i

   do i=1,Nx
    f(i) = sin(x(i))
   enddo

   do i=1,10**5
     call setX(f1,f) ! 
     f = getX(f1) ! Should I use this? 
     fp = getX(f1) ! Or this?
     fp => getX(f1) ! Or even this?
   enddo
   end program

目前,我在 Windows 上运行。当我 CTR-ALT-DLT 并查看性能时,“物理内存使用历史”会随着每次循环迭代而增加。这就是为什么我假设我有内存泄漏。

所以我想提出我的问题:这是内存泄漏吗? (内存随着上述每一种情况而增加)。如果是这样,有没有办法在仍然使用指针的同时避免内存泄漏?如果不是,那么发生了什么,我应该担心吗?有没有办法降低这种行为的严重性?

很抱歉最初的含糊问题。我希望这更重要。

【问题讨论】:

  • 我建议尽可能远离返回指针的函数。它们很容易出错。
  • 可以公平地说,在子程序中返回指针可以更安全地实现吗?我在下面的评论中提到,我必须使用指针..
  • 是的,使用子例程或可分配对象

标签: pointers memory memory-management fortran fortran90


【解决方案1】:

您真的仅限于 Fortran 90 吗?在 Fortran 2003 中,您将为此使用可分配函数结果。这要安全得多。使用指针函数结果,此代码是否存在内存泄漏取决于您如何引用该函数,而您没有显示。如果您必须从过程中返回一个指针,那么通过子例程参数返回它会更安全。

但是……

这个函数没有意义。 您在上一行中将它作为 SHAPE 的参数引用之后,测试它的关联状态是没有意义的。如果指针组件已解除关联(或具有未定义的指针关联状态),则不允许您引用它。

另外,如果关联了指针组件,你只需调用stop!

也许你把问题的代码抄错了?


如果您只是删除以if (associated(this%vals3D))... 开头的整个 if 构造,那么您的代码可能有意义。

但是……

  • 如果this%TF3D 为真,则必须关联this%vals3D
  • 引用函数时,必须使用指针赋值

    array_ptr => getValues3D(foo)
    !          ^
    !          |
    !          + this little character is very important.
    

    忘记那个小字符,你正在使用正常的分配。语法上有效,在阅读代码时难以区分,在这种情况下,除了使用指针的常见缺陷(例如,您需要 DEALLOCATE array_ptr 在你重用它或它超出范围之前)。这就是为什么返回指针结果的函数被认为是有风险的。


您的完整代码显示了几个内存泄漏。每次你分配一个 POINTER 的东西时——你几乎需要保证会有一个匹配的 DEALLOCATE。

您的测试代码中有一个循环。 ALLOCATE 被调用了很多 - 在 both setter 和 getter 中。匹配的 DEALLOCATE 语句在哪里?

【讨论】:

  • 我仅限于使用 Fortran 90。在 Fortran 90 的派生数据类型中不允许使用可分配数组。我同意并且知道是否存在内存泄漏是有帮助的取决于我如何引用该函数。我应该包含这个函数的实现,但它的作用只是充当一个 getter 函数。我从我发布的网站种植了sn-p,发现警告出现了。
  • 您链接到的问题的代码示例中没有函数 - 因此您不能只是“植入”代码。但除此之外 - 链接问题中的逻辑与代码中的逻辑不匹配 - 也许您混淆了派生类型中的函数结果和指针组件?
  • 我忘了接受这个问题的任何答案,我决定这个答案让我朝着正确的方向前进:不要使用函数返回分配的指针;他们太危险了。就像我们在stackoverflow.com/questions/24091998/… 中讨论的那样。再次感谢您。
【解决方案2】:

每次调用setX 时,之前为您的类型的x 组件分配的任何内存都将被泄露。由于您调用该函数 10^5 次,您将浪费 100000-1 个副本。如果您知道this%x 的大小永远不会改变,只需通过检查ASSOCIATED(this%x) 是否为真来检查以前的调用是否已经分配了内存。如果是,跳过分配并直接移动到赋值语句。如果大小确实发生了变化,那么您首先必须在分配新空间之前释放旧副本。

setX 上的另外两个次要 cmets:虚拟参数 xTARGET 属性似乎是多余的,因为您从不使用该参数的指针。其次,您的类型的 TFx 组件似乎也是多余的,因为您可以检查是否分配了 x

对于函数getX,为什么不完全跳过分配,只设置res => this%x?诚然,这将返回对基础数据的直接引用,这可能是您想要避免的。

在你的循环中,

做 i=1,10**5 调用 setX(f1,f) ! f = getX(f1) !我应该使用这个吗? fp = getX(f1) !或这个? fp => getX(f1) !甚至这个? 结束

fp => getX(f1) 将允许您获得指向您的类型的基础x 组件的指针(如果您采用我上面的更改)。其他两个使用赋值运算符并将数据从getX 的结果复制到f 或(如果之前已分配)fp。如果fp没有分配,代码会崩溃。

如果您不想授予对底层数据的直接访问权限,那么我建议将getX 的返回值定义为自动数组,其大小由this%x 确定。也就是说,你可以把函数写成

函数 getX(this) 结果(res) 隐式无 类型(vecField1D),意图(in)::这个 真实(dpn),尺寸(大小(this%x,1)):: res res = 这个%x 结束函数

【讨论】:

    猜你喜欢
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-15
    • 2013-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多