由于主程序中的索引重命名,子例程未在假定形状数组中返回正确的数值

Subroutine not returning correct numerical values in assumed shape array due to index renaming in the main program

我的 Fortran 95 子例程的参数是一个假设的形状数组,意图为:

the_subroutine(my_argument)
real, dimension(:,:), intent(inout) :: my_argument
(...)

在主程序中,我有一个可分配数组。我分配它 并重命名索引 。然后我调用子例程并将该(正确分配的)数组传递给子例程:

allocate(the_array( 5:1005 , 5:1005 ))
call the_subroutine(my_argument=the_array)

子例程进行某些计算并用值填充数组。在 子程序结束前的最后一行 ,我检查了一个随机值:

(...)
print*, my_argument(213,126) ! I get 2.873...
end subroutine the_subroutine

然后,在子程序调用后的第一行,我检查子程序是否已将值正确地传递给外部世界,但事实并非如此:

call the_subroutine(my_argument=the_array)
print*, the_array(213,126) ! I get 3.798... A completely different value.

问题是由于在主程序中将数组重新索引为:

allocate(the_array( 5:1005 , 5:1005 ))

其中 max_index - min_index = 1000-1,但是子程序 "sees" 数组在内部就好像我已经声明了正常的方式,即:

allocate(the_array( 1:1000, 1:1000))

或者简单地说,分配(the_array( 1000, 1000 ))

因此,内部数组中的元素(213,126)与主程序数组中的位置不同。有什么简单的方法可以解决这个问题吗?

假定形状数组的默认下限为一。

如果您想要不同的下限,则需要适当地声明数组虚拟参数。

subroutine the_subroutine(my_argument)
  real, dimension(5:,5:), intent(inout) :: my_argument
!                 ^  ^

(延迟形状数组的虚拟参数边界规则不同 - 数组虚拟参数也具有 POINTER 或 ALLOCATABLE 属性。)

使用lbound将边界传递给子例程:

  implicit none
  real,allocatable:: x(:,:)
  allocate(x(5:10,5:10))
  call sub(x,lbound(x))
  write(*,*)'o',x(5,5)
  contains

  subroutine sub(x,lb)
  implicit none
  integer lb(2)
  real, dimension(lb(1):,lb(2):)::x
  x(5,5)=42.
  end subroutine
  end

o 42.0000

终于找到了解决方法。

首先,如果在 Fortran 2003(或具有非标准扩展的 Fortran 95)中工作,您可以简单地将子例程中假定的形状参数声明为 ALLOCATABLE:

subroutine the_subroutine(my_argument)
real, dimension(:,:), allocatable, intent(inout) :: my_argument

然后子程序"sees"正确地重命名了索引。然而,这在 Fortran 95 标准中是不允许的。

在 Fortran 95 中,我找到的最优雅的方法是使用指针:

program example
implicit none
real, dimension(:,:), allocatable, target  :: the_array
real, dimension(:,:),              pointer :: the_pointer
[...]
allocate(the_array(5:1005,5:1005))
the_pointer => the_array
call the_subroutine(my_argument=the_pointer)

并且在子程序中:

subroutine the_subroutine(my_argument)
real, dimension(:,:), pointer :: my_argument

那就完美了。在子例程中,MY_ARGUMENT 被完全视为假设的形状数组。

另一种解决问题的方法:使数组成为派生类型,子例程成为该类型的方法:

  module example
  implicit none
  type a
  real, allocatable::y(:,:)
  end type a
  contains
  subroutine sub(this)
  type(a)::this
  write(*,*)lbound(this%y)
  end subroutine
  end module

  program p
  use example
  implicit none
  type (a)  my_array
  allocate(my_array%y(5:6,7:8))
  call sub(my_array)
  deallocate(my_array%y)
  allocate(my_array%y(2:3,1:2))
  call sub(my_array)
  end

5 7

2 1

如您所见,子程序会自动识别正确的尺寸。显然现在子程序只能使用派生类型数组。