具有 Fortran 90/95 旧代码的接口 (#6634) 出错



我已经使用科学(学术)软件进行机械分析工作了相当长的一段时间。现在,代码从八十年代(Fortran 77)开始,并以Fortran 90/95的混合/混合形式到达我。但是,由于需要添加像 MKL 这样的强大工具,我决定从旧的 Intel Visual Fortran 11.072(使用 VS2008)迁移到"最新"的 14.0(在 ComposerXE 2013 中)。F77 内核编译没有问题,但我在处理变量延迟定义的子例程中的一些接口时遇到了麻烦。我没有为庞大的例行公事而烦恼,而是制作了一个能够重复这些问题的 MWE。

小程序复制如下,所以你可以修改它:

  program main
  implicit none
  print *, 'Start of the program'
  call mainsub
  print *, 'End of the program'
  pause
  end program

有一个定义大小的模块问题.f:

  module problem
  implicit none
  save
  integer, parameter :: size1 = 6
  integer, parameter :: size2 = 3
  integer, parameter :: size3 = 18
  end module problem

因此,有一个主要的子例程调用"第一级"子例程:

  SUBROUTINE mainsub
  use problem  ! here there are the dimensions defined
  implicit none
  ! Scalars (almost)
  REAL*8  :: sca01, sca02(size2), sca03
  ! Vectors
  REAL*8  :: arr01(size1)
  REAL*8  :: arr02(size1)      
  REAL*8  :: arr03(size3)
  REAL*8  :: arr04(size1)
  ! Matrices
  REAL*8  :: mat01(size1,size1)
  REAL*8  :: mat02(size3)
  ! trying to trick IFORT with interface (hiding dimension)
  print *, 'Calling sub11'
  CALL sub11(arr01)
  print *, 'Calling sub11 - end'
  pause
  print *, 'Calling sub12'
  CALL sub12(arr02,arr03,arr04)
  print *, 'Calling sub12 - end'
  pause
  print *, 'Calling sub13'
  CALL sub13(mat01,mat02)
  print *, 'Calling sub13 - end'
  pause
  print *, 'Calling sub14'
  CALL sub14(sca01,sca02,sca03)
  print *, 'Calling sub14 - end'
  pause
  contains 
      subroutine sub11(arr01)
      use problem
      implicit none
      REAL*8, DIMENSION(:) :: arr01
      print *, 'This is sub11, size arr01: ', SIZE(arr01), SHAPE(arr01)
      CALL sub21(arr01)
      end subroutine
  end subroutine

这些是"第一级"子例程

  SUBROUTINE sub12(arr02, arr03, arr04)
  use problem
  implicit none
  REAL*8  :: arr02(*)
  REAL*8  :: arr03(size3)
  REAL*8  :: arr04(*)
  REAL*8 :: dummy(600)
  print *, 'sub 12'
  call sub22(arr02, dummy, arr04)
  END SUBROUTINE

  SUBROUTINE sub13(mat01,mat02)
  use problem
  implicit none
  REAL*8  :: mat01(size1,size1)
  REAL*8  :: mat02(size3,*)
  print *, 'sub 13'
  call sub23(mat01, mat02)
  END SUBROUTINE

  SUBROUTINE sub14(sca01,sca02,sca03)
  use problem
  implicit none
  REAL*8  :: sca01, sca02(*), sca03
  REAL*8 :: dummy(600)
  print *, 'sub 14'
  call sub24(sca01, dummy, sca03)
  END SUBROUTINE

最后,这些是"第二级"子例程:

  SUBROUTINE sub21(arr01)
  use problem
  implicit none
  REAL*8 :: arr01(size3,size1)
  print *, 'This is sub21, size arr01: ', SIZE(arr01)
  END SUBROUTINE

  SUBROUTINE sub22(arr02, arr03, arr04)
  use problem
  implicit none
  REAL*8  :: arr02(size3)
  REAL*8  :: arr03(size3)
  REAL*8  :: arr04(size2,size3)
  print *, 'sub22'
  print *, SIZE(arr02)
  print *, SIZE(arr03)
  print *, SIZE(arr04)
  END SUBROUTINE

  SUBROUTINE sub23(mat01,mat02)
  use problem
  implicit none
  REAL*8  :: mat01(size1,size2)
  REAL*8  :: mat02(size1,size2,size3)
  print *, 'sub 23'
  print *, SHAPE(mat01), SIZE(mat01)
  print *, SHAPE(mat02), SIZE(mat02)
  end subroutine

  SUBROUTINE sub24(sca01,sca02,sca03)
  use problem
  implicit none
  REAL*8  :: sca01, sca02(*), sca03
  print *, 'sub 24'
  print *, SHAPE(sca01), SHAPE(sca03)
  end subroutine

此代码在我的机器上使用英特尔 Fortran 14 正确编译。现在,让我们考虑一下可能出现的一系列情况。

常见变量不匹配如果我将一个实际变量定义为 Real*8,并且在子例程中相应的虚拟变量是 Real*4,或者混合 Real*8 --> Integer*8,编译器会识别不匹配并给出错误。同样,如果我定义一个标量变量 Real sca01 并在子例程中定义它 Real sca01(*) 或 Real sca01(size1),编译器会再次识别出一个是数组,另一个不是,因此它会抛出错误。(错误 #6633:实际参数的类型与虚拟参数的类型不同。

阵列大小不匹配如果将一个数组定义为 arr02(size1) 并在调用的子例程 arr02(size2) 中,则仅当运行时检查错误处于活动状态并且整数 size1,size2 声明为参数(如在模块 problem.f 中)时,编译器才会注意到不匹配。

但是,我将一个中间子例程放在两个定义的中间,如上面的 MWE 所示:

            sub11 -- sub21
          /
mainsub --- sub12 -- sub22
          
            sub13 -- sub23

带有 CONTAINS 语句(mainsubsub11)的接口检查实际变量和虚拟变量的大小和维度是否一致。但是,它不会检查对 sub21 的下一次调用在从接口子例程中退出时是否丢失了大小跟踪。

sub12 中,通过使用假定的形状数组 (*) 定义,我可以根据需要更改形状和大小。当然,我会在某个时候遇到分段错误,但即使我打开了所有运行时检查和恒定大小,也不会抛出错误或警告。

最后,对于 sub12,这个技巧也适用于多个维度,有时即使它不是形状假设的数组,就像 mat02 的情况一样(这很奇怪......

因此,我有几个问题:

  1. 带有 ( * ) 和 ( : ) 的接口定义之间的区别是什么?

  2. 如果 ( * ) 类似于数组大小的延迟定义,为什么它不适用于标量?(假设标量是数组 1x1)为什么它不通过子例程检查大小?

  3. 在英特尔 Fortran 11 中,没有对阵列进行许多大小检查。现在,使用英特尔 Fortran 14,我有很多 #6633、#6634 和 #8284 错误。它改变了什么?

  4. 鉴于此混合 Fortran77/90/95 全景图,我应该考虑维护哪些定义,哪些不考虑?(显然在上面使用的并且没有进入面向对象,因为它是一个过程程序)

  5. 那么Fortran中变量定义的哲学(o主要实践原因)是什么?如果我可以用程序程序(以及恒定的大小,没有可分配的大小)"欺骗"编译器,我想我错过了一些东西。

我想为问题的长度道歉,但是独自学习了Fortran并且没有CS背景,我觉得我错过了这个问题的要点......

谢谢!

  1. 对于虚拟参数,dummy(*)声明假定大小数组,dummy(:)声明假定形状数组[为了完整起见,dummy(some_expression)声明显式形状数组]。

    对于假定大小的数组:

      实际参数
    • 必须是可以生成数组元素序列的东西,但实际参数的维度不需要与虚拟人的维度相同。

    • 在使用虚拟参数
    • 的过程中,实际参数的大小不会自动知道,并且您不能对需要该大小的虚拟参数调用操作(例如 不允许SIZE(dummy)。 如果由于某种原因在过程中需要实际参数指定的数组元素序列的大小,则用户需要单独传递它。

    • 调用过程的作用域中不需要显式接口。 (假定大小是Fortran 77中的一个概念,在语言具有显式接口的概念之前。

    对于假定的形状数组:

    • 实际参数的排名必须与虚拟参数匹配。

    • 在过程中,实际参数的大小和形状可以通过虚拟自动获得。

    • 在调用该过程的任何作用域中都需要过程的显式接口。

    显式大小数组

    类似于假定大小数组,但在过程中数组的大小是已知的(因为它是显式声明的! 实际参数指定的数组元素序列的大小必须等于或大于显式指定的大小。

  2. 作为标量变量名称(不是 CHARACTER 类型且默认或C_CHAR种类)的实际参数不指定数组元素序列。 作为一般原则,标量不是数组。

  3. 改进了
  4. 编译器在程序中查找错误的能力。 此外,某些与错误检查相关的编译器选项可能已更改默认值。 (请注意,不同的编译器在这方面具有不同的诊断功能。

  5. 哪种类型的数组声明(除了这里讨论的声明之外还有其他声明)最好取决于您要执行的操作,但作为新代码的准则,如果假定形状周围的限制不会导致问题,则对数组使用假定形状。

  6. 不能保证编译器会捕获所有编程错误。 不同的声明用于不同的目的。

最新更新