如何将可分配数组传递给 Fortran 中的子例程

Posted

技术标签:

【中文标题】如何将可分配数组传递给 Fortran 中的子例程【英文标题】:How to pass allocatable arrays to subroutines in Fortran 【发布时间】:2012-10-15 01:19:57 【问题描述】:

以下代码返回分段错误,因为我尝试传递的可分配数组没有被正确识别(大小返回 1,而它应该是 3)。在此页面 (http://www.eng-tips.com/viewthread.cfm?qid=170599) 中,一个类似的示例似乎表明它在 F95 中应该可以正常工作;我的代码文件扩展名为 .F90,但我尝试将其更改为 F95,并且我正在使用 gfortran 进行编译。

我的猜测是问题应该出在我将可分配数组传递给子程序的方式上;我做错了什么?

!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%!
 PROGRAM test
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%!
 IMPLICIT NONE
 DOUBLE PRECISION,ALLOCATABLE :: Array(:,:)
 INTEGER                      :: iii,jjj

 ALLOCATE(Array(3,3))
 DO iii=1,3
 DO jjj=1,3
    Array(iii,jjj)=iii+jjj
    PRINT*,Array(iii,jjj)
 ENDDO
 ENDDO
 CALL Subtest(Array)

 END PROGRAM
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%!
 SUBROUTINE Subtest(Array)
 DOUBLE PRECISION,ALLOCATABLE,INTENT(IN) :: Array(:,:)
 INTEGER                                 :: iii,jjj

 PRINT*,SIZE(Array,1),SIZE(Array,2)
 DO iii=1,SIZE(Array,1)
 DO jjj=1,SIZE(Array,2)
    PRINT*,Array(iii,jjj)
 ENDDO
 ENDDO

 END SUBROUTINE
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%!

【问题讨论】:

请注意,可分配的虚拟参数实际上是 Fortran 2003 的一项(广泛支持 - 包括 gfortran 已有几年)功能。 【参考方案1】:

如果一个过程有一个可分配的虚拟参数,那么在任何调用范围内都需要一个显式接口。

(有很多东西需要显式接口,可分配的虚拟对象就是其中之一。)

您可以通过在主程序中为您的子例程放置一个接口块来自己提供该显式接口。另一种更好的选择是将子例程放在模块中,然后在主程序中使用该模块-然后自动创建显式接口。在您提供了链接的 eng-tips 网站上有一个示例 - 请参阅 xwb 的帖子。

请注意,只有当您要执行与其分配状态相关的操作时,虚拟参数才有意义具有可分配属性 - 查询其状态、重新分配、解除分配等。

【讨论】:

谢谢,这是我的问题。我知道某些函数(不记得它们的名称,但我认为它们是更改输入参数的函数)需要显式接口,但不知道分配是相同的。是的,代码是为了调试我的真实代码而编写的。【参考方案2】:

还请注意,您的可分配伪参数array 是用intent(in) 声明的,这意味着它的分配状态将是关联的实际参数的状态(并且在过程中可能不会更改)。传递给子例程的实际参数可能未分配,因此即使使用显式接口也无法引用。编译器不会知道这一点,在这种情况下,size 等查询的行为是未定义的。

因此,在引用其内容之前,您首先必须检查arrayallocated(array) 的分配状态。我会进一步建议使用lboundubound 在整个数组上实现循环,因为通常你不能确定array 的边界:

subroutine subtest(array)
  double precision, allocatable, intent(in) :: array(:,:)
  integer                                   :: iii, jjj

  if(allocated(array)) then
    print*, size(array, 1), size(array, 2)
    do iii = lbound(array, 1), ubound(array, 1)
      do jjj = lbound(array, 2), ubound(array, 2)
        print*, array(iii,jjj)
      enddo
    enddo
  endif  
end subroutine

【讨论】:

注意:将此添加为新答案,因为我还不能评论其他人。我认为这更像是对 IanH 答案的扩展,因为 xwb 在 eng-tips.com 的帖子中也遗漏了这种微妙之处。我不想闯入大编辑;不过,请随意将其合并为一个答案。 ubound 似乎与 Size 具有相同的效果;有什么区别?并且使用 lbound 是为了防止数组以高于 1 的索引开始?这会发生吗? size 给出数组元素的总数(不带 dim 参数)或沿任意维度的数量(带 dim 参数)。您可以分配具有边界(-2:0, -2:0) 的数组,并且每个维度的大小仍为3,但array(1:3, 1:3) 未定义。这与具有假定形状的不可分配虚拟数组形成对比,如果未指定,则下限确实假定为 1。【参考方案3】:

这是一个简单的示例,它在模块中使用可分配的虚拟参数。

module arrayMod   
  real,dimension(:,:),allocatable :: theArray    
end module arrayMod

program test
   use arrayMod
   implicit none

   interface
      subroutine arraySub
      end subroutine arraySub
   end interface

   write(*,*) allocated(theArray)
   call arraySub
   write(*,*) allocated(theArray) 
end program test

subroutine arraySub
   use arrayMod

   write(*,*) 'Inside arraySub()'
   allocate(theArray(3,2))
end subroutine arraySub

【讨论】:

以上是关于如何将可分配数组传递给 Fortran 中的子例程的主要内容,如果未能解决你的问题,请参考以下文章

在 FORTRAN 子例程中传递不同的变量集

将指向 uint16_t 的指针传递给需要 C 中 uint8_t[] 数组的子例程 - 如何?

如何让 VBA 子例程调用将数组传递给子例程中的另一个函数的函数

fortran 模块的子例程名称冲突

在 C++ 和 Fortran 代码之间传递复数数组

如何在由 MPI 并行化的 fortran 中调用子例程?