Fortran视觉堡垒和gfortran之间的Fortran 90区别

时间:2014-06-06 23:21:38

标签: pointers memory-management fortran fortran90 gfortran

这可能是一个特定的问题,但我认为它与如何使用这两个编译器(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。我知道主程序似乎没有做任何有建设性的事情,但重点是我不认为循环应该受内存的限制,而应该是运行时的函数。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:2)

此代码存在一些问题,可能是由于对语言的误解造成的。这些问题与特定的编译器无关 - 代码本身就被破坏了。

从概念上讲,请注意:

  • Fortran 90中每个程序的过程中只有一个已保存变量的实例。
  • 每次调用函数时,表示函数结果的变量总是从undefined开始。
  • 如果希望调用范围中的指针指向具有指针结果的函数的结果,则必须使用指针赋值。
  • 如果指定了指针,则需要匹配deallocate。

存在潜在的逻辑错误,因为first_entrygetX过程中保存的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坦率地说是荒谬的