Указатель возврата функции Fortran 90
Я видел этот вопрос:
и принятый ответ заставил меня задаться вопросом, если бы я написал следующую функцию безопасно (не допуская утечки памяти)
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 конечная функция