Указатель возврата функции Fortran 90

Я видел этот вопрос:

Fortran динамические объекты

и принятый ответ заставил меня задаться вопросом, если бы я написал следующую функцию безопасно (не допуская утечки памяти)

   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 и просматриваю производительность, "история использования физической памяти" увеличивается с каждой итерацией цикла. Вот почему я предполагаю, что у меня утечка памяти.

Поэтому я хотел бы ответить на мой вопрос: это утечка памяти? (Память увеличивается с каждым из вышеперечисленных случаев). Если так, есть ли способ избежать утечки памяти при использовании указателей? Если нет, то, что происходит, следует ли мне беспокоиться и есть ли способ уменьшить серьезность этого поведения?

Извините за первоначальный неопределенный вопрос. Я надеюсь, что это ближе к делу.

2 ответа

Решение

Вы действительно ограничены Fortran 90? В Фортране 2003 вы бы использовали для этого выделенную функцию. Это намного безопаснее. Использование функции указателя приводит к тому, что у вас есть утечка памяти с этим кодом или нет, зависит от того, как вы ссылаетесь на функцию, которую вы не показываете. Если вы должны вернуть указатель из процедуры, гораздо безопаснее вернуть его через аргумент подпрограммы.

НО...

Эта функция бессмысленна. Нет смысла проверять статус ассоциации этого%vals3D` после того, как вы ссылаетесь на него в качестве аргумента SHAPE в предыдущей строке. Если компонент указателя отсоединен (или имеет неопределенный статус ассоциации указателя), то вам не разрешено ссылаться на него.

Кроме того, если компонент указателя связан, все, что вам нужно сделать, это остановить вызов!

Возможно, вы неправильно расшифровали код вопроса?


Если вы просто удалите всю конструкцию if, начинающуюся с if (associated(this%vals3D))... тогда ваш код может иметь смысл.

НО...

  • если this%TF3D верно, то this%vals3D должен быть связан.
  • когда вы ссылаетесь на функцию, вы должны использовать указатель

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

    Забудьте этого маленького персонажа, и вы используете обычное задание. Синтаксически действительный, трудно определить разницу при чтении кода и, в этом случае, потенциально источник повреждения памяти или утечки, которые могут остаться незамеченными до самого худшего момента, в дополнение к обычным ошибкам использования указателей (например, вам нужно DEALLOCATE array_ptr, прежде чем использовать его повторно или он выходит из области видимости). Вот почему функции, возвращающие результаты указателя, считаются рискованными.


Ваш полный код показывает несколько утечек памяти. Каждый раз, когда вы выделяете что-то, что является указателем - вы должны в значительной степени гарантировать, что будет соответствующий DEALLOCATE.

У вас есть цикл в вашем тестовом коде. ALLOCATE часто вызывается - как в установщике, так и в получателе. Где находятся соответствующие операторы DEALLOCATE?

Каждый раз setX называется, любая ранее выделенная память для x Компонент вашего типа будет утечка. Так как вы вызываете функцию 10^5 раз, вы будете тратить 100000-1 копий. Если вы знаете, что размер this%x никогда не изменится, просто проверьте, не выделил ли предыдущий вызов память, проверив, ASSOCIATED(this%x) правда. Если это так, пропустите выделение и перейдите непосредственно к оператору присваивания. Если размер действительно изменится, вам придется сначала освободить старую копию, прежде чем выделять новое место.

Два других незначительных комментария setX: TARGET атрибут фиктивного аргумента x кажется излишним, так как вы никогда не берете указатель на этот аргумент. Во-вторых, TFx компонент вашего типа также кажется излишним, так как вы можете вместо этого проверить, если x выделено.

Для функции getXпочему бы не пропустить выделение полностью, а просто установить res => this%x? По общему признанию, это возвратит прямую ссылку на лежащие в основе данные, которые, возможно, вы хотите избежать.

В вашем цикле,

   сделать я =1,10**5
     вызовите setX(f1,f)! 
     f = getX(f1)! Должен ли я использовать это? 
     fp = getX(f1)! Или это?
     fp => getX(f1)! Или даже это?
   ENDDO

fp => getX(f1) позволит вам получить указатель на базовый x компонент вашего типа (если вы примете мое изменение выше). Два других используют операторы присваивания и будут копировать данные из результата getX в любой fили (если он был ранее выделен) fp, Если fp не выделен, код будет сбой.

Если вы не хотите предоставлять прямой доступ к базовым данным, то я предлагаю, чтобы возвращаемое значение getX должен быть определен как автоматический массив с размером, определяемым this%x, То есть вы можете написать функцию как

   функция getX(this) результат (res)
     неявный
     тип (vecField1D), намерение (in):: this
     реальный (dpn), размерность (размер (это%x,1)):: res
     res = это% x
   конечная функция
Другие вопросы по тегам