如果我将分配的数组传递给维度不匹配的子例程,如何让Fortran警告我



我只是花了太多时间来解决程序中的一个错误。我调用一个子程序(让我们调用mysub),定义为:

subroutine mysub(hourval,dayval,some,more,args)
use globals
implicit none
real:: hourval(24,ncol,nrow)
real:: dayval(ncol,nrow)
...
end

其中,hourvaldayval是包含子程序中计算输出的数组,nrowncol是整数。

对子程序的调用看起来像

call mysub(daygrid,hourgrid,some,more,args)

其中daygridhourgrid是先前为allocated的阵列。hourgrid具有三维(24,ncol,nrow),而daygrid具有二维(ncol,nrow)。到目前为止提到的所有变量(hourgriddaygridnrowncol)都在模块globals中声明。

我不断得到奇怪的结果,最后注意到我在子程序调用中切换了hourgriddaygrid的顺序,改变这一点解决了我的问题(我的程序中的实际变量的名称不太明确,这使得错误更难发现)。然而,程序会正常编译,并且我不会在日志文件中得到任何相关的错误消息。

因此,我想知道是否有一种方法可以在编译时或运行时获取指向该错误的消息。我在Linux上使用的是带有以下编译器标志的"英特尔Fortran 11.1":

-O3 -C -pg -traceback -g

实际上,我指望-C标志(相当于-check all)来检测这种错误,因为它暗示了选项-check bounds。我还能做什么吗?

您可能需要使用假定形状的伪参数来捕获类似这样的错误(即real, dimension(:,:) :: dayvalreal, dimension(:,:,:) :: hourval。)

摘自Metcalf、Cohen和Reid(2011)解释的现代Fortran

假设伪参数为形状时,不会发生形状或字符不一致。

此外,通过use语句将已经可用的变量作为实际参数传递给同一个子例程,这并不是最令人惊叹的编码风格。

如果当时数组大小"已知",编译器可能会在编译时注意到这一点。您的管理方式取决于您的代码。

如果有一个(显式或隐式)接口可用,ifort应该为您做到这一点。

即使没有接口,您也可能会对编译选项-warn interface感到高兴:这本质上比通常检查接口是否"明显"(即使不"可用")要好。[我希望有人能解释得更好。]

可能值得注意的是,除非接口出现故障,否则列数不一定是问题。相反,它取决于存储范围。在您的情况下,存储空间大不相同。[1]

作为对您评论的回应,请介绍一些关于接口的内容。

您已经在使用模块,所以如果mysub位于调用主机的(另一个)模块use d中,则将提供显式接口。这是一件好事。许多细节可以在界面说明的SO上找到。

最后,我要说的是,即使hourgriddaygrid是在模块中声明的,但这些名称的伪参数是不同的。

编辑,更明确一点。

[1] 解决尺寸"不匹配"的问题通常是一个更困难的问题。Fortran 2008标准在第12.5.2.11节中指出,当涉及到子程序声明时(我强调)

如果伪参数是显式形状或假定大小的数组,则表示元素序列并对应于作为数组的伪参数的实际参数是与伪参数关联的序列实际自变量的秩和形状不必与伪自变量的秩或形状一致,但伪自变量中的元素数量不得超过实际自变量的元素序列中的元素数

你的假人是明确的形状。因此,通常情况下,您无法检测到不匹配,但在这种情况下,伪hourvaldayval实际参数大(乘以24),这是允许编译器提供帮助的部分。

更普遍地说,在使用显式形状的假人时必须小心。

使用假设形状的建议真的很好(所以有+1),因为这将检测形状不匹配。然而,如果您的数组具有相同的形状,那么您可能会再次遇到麻烦。原因是,如果参数与接口中的参数匹配,编译器就无法知道您打算做什么。

因此,避免该问题的更好解决方案可能是使用命名参数(除了使用具有显式接口的假定形状之外)。在下面的示例中,sunmoon是要处理的类似阵列。当您切换它们时,子例程调用仍然有效,但会交换数组的使用。使用命名参数时,无论您将它们按何种顺序排列,子例程都会将其连接到正确的伪参数。

program named_arguments
integer, parameter :: n = 2
integer, allocatable :: sun(:,:), moon(:,:)
allocate(sun(n,n))
allocate(moon(n,n))
call mysub(sun,moon)
write(*,*) sun
write(*,*) moon
call mysub(moon,sun)
write(*,*) sun
write(*,*) moon
call mysub(night=moon,day=sun)
write(*,*) sun
write(*,*) moon
contains
subroutine mysub(day,night)
integer :: day(:,:), night(:,:)
day = 1
night = 2
end subroutine
end program

由于您可能会为反映其用法的伪参数提供有意义的名称,因此通过名称将它们连接到调用中的实际参数是避免此类问题的良好做法。

在您的情况下:

call mysub(dayval=daygrid,hourval=hourgrid,...)

会做正确的事情,因为使用命名参数不依赖于伪参数的顺序。

最新更新