这可能是一个特定的问题,但我认为它与如何使用这两个编译器(Compaq visual Fortran Optimizing Compiler Version 6.5和minGW)处理内存有关。我试图通过在Fortran 90中使用指针来了解最佳实践(我必须使用它)。这是一个示例代码,应该可以正常使用"开箱即用"来自gfortran编译器的一个警告:" POINTER值函数出现在赋值"的RHS上,而没有来自其他编译器的警告。
module vectorField_mod
implicit none
type vecField1D
private
real(8),dimension(:),pointer :: x
logical :: TFx = .false.
end type
contains
subroutine setX(this,x)
implicit none
type(vecField1D),intent(inout) :: this
real(8),dimension(:),target :: x
logical,save :: first_entry = .true.
if (first_entry) nullify(this%x); first_entry = .false.
if (associated(this%x)) deallocate(this%x)
allocate(this%x(size(x)))
this%x = x
this%TFx = .true.
end subroutine
function getX(this) result(res)
implicit none
real(8),dimension(:),pointer :: res
type(vecField1D),intent(in) :: this
logical,save :: first_entry = .true.
if (first_entry) nullify(res); first_entry = .false.
if (associated(res)) deallocate(res)
allocate(res(size(this%x)))
if (this%TFx) then
res = this%x
endif
end function
end module
program test
use vectorField_mod
implicit none
integer,parameter :: Nx = 15000
integer :: i
real(8),dimension(Nx) :: f
type(vecField1D) :: f1
do i=1,10**4
f = i
call setX(f1,f)
f = getX(f1)
call setX(f1,f)
if (mod(i,5000).eq.1) then
write(*,*) 'i = ',i,f(1)
endif
enddo
end program
该程序在两个编译器中运行。但是,将循环从10 ** 4更改为10 ** 5会导致gfortran出现严重的内存问题。
使用CTR-ALT-DLT,打开"性能",在gfortran中运行时,物理内存会迅速增加,并且似乎没有为compaq编译器移动。我经常在计算机崩溃之前取消,因此在达到最大值后我不确定该行为。
这似乎不是使用指针的合适方法(我在派生数据类型中需要它)。所以我的问题是:如何在保持相同类型的界面和功能的同时安全地使用指针?
P.S。我知道主程序似乎没有做任何有建设性的事情,但重点是我不认为循环应该受内存的限制,而应该是运行时的函数。
非常感谢任何帮助。
答案 0 :(得分:2)
此代码存在一些问题,可能是由于对语言的误解造成的。这些问题与特定的编译器无关 - 代码本身就被破坏了。
从概念上讲,请注意:
存在潜在的逻辑错误,因为first_entry
和getX
过程中保存的setX
变量与setX
过程和过程中的对象特定状态混为一谈getX
过程中的实例特定状态。
第一次调用setX
特定x
对象的this
指针组件将由于if语句而无效(那里也存在风格不佳的问题 - 是小心在if语句后面有多个语句 - 它只是第一个受条件限制的!)。如果再次使用不同的 setX
调用this
,则first_entry将设置为false,并且this
对象将无法正确设置。我怀疑你应该测试this%TFX
。
类似地,第一次调用getX
否则未定义的函数结果变量res
将被取消。但是,在所有后续调用中,函数结果都不会无效(函数结果在每次执行函数时都会启动未定义),然后会在相关测试中错误地使用,也可能在deallocate语句中错误地使用。 (在具有未定义关联状态的指针上调用关联(或解除分配)是非法的 - 注意未定义的关联状态与解离的状态不同。)
getX
返回一个指针结果 - 一个由分配的指针创建的结果。然后该指针丢失,因为“正常”赋值用于访问评估函数所得的值。因为这个指针丢失了,所以不能(并且没有...)匹配的deallocate语句来反转指针分配。因此该程序泄漏了内存。几乎可以肯定的是,在主程序中捕获getX函数值的东西(在本例中为f
,但f
用于多个事物,所以我称之为f_ptr
...)本身应该是一个指针,它应该是指针分配 - f_ptr => getX(f1)
。在随后的setX调用和写入语句中使用f_ptr
的值之后,可以显式释放它。
在指定指针时意外使用正常赋值的可能性是不鼓励使用带指针结果的函数的原因之一。如果需要返回指针 - 那么使用子程序。
Fortran 95允许将这些组件的默认初始化设置为NULL,从而简化了指针组件的管理。 (请注意,您在类型定义中使用默认初始化 - 因此您的代码不是Fortran 90!)
Fortran 2003(或Fortran 95 +可分配TR - 这是大多数维护编译器支持的语言级别)引入了可分配的函数结果 - 这可以消除许多可能使用指针函数产生的潜在错误。
Fortran 95 +可分配的TR支持如今无处不在,并且语言改进和修复对于这一点非常有用(除非你在某种模糊的平台上运行)将语言级别限制为Fortran 90坦率地说是荒谬的