【问题标题】:Array inside type array as function argument类型数组内的数组作为函数参数
【发布时间】:2012-08-16 05:19:19
【问题描述】:

我手头有以下程序

program foo
  type bar
    real, dimension(2) :: vector
  end type
  type(bar), dimension(3) :: bararray
  call doSomething(bararray%vector)
end program

subroutine doSomething(v)
  real, dimension(3,2), intent(inout) :: v
  ...
end subroutine

现在这给了我一个编译错误。

Error: Two or more part references with nonzero rank must not be specified at (1)

如果我将调用更改为

call doSomething((/bararray%vector(1), bararray%vector(2)/))

一切都很顺利。问题是这看起来有点麻烦,所以问题是,有没有其他方法可以为子程序编写参数?

提前致谢。

【问题讨论】:

    标签: fortran argument-passing fortran90 gfortran


    【解决方案1】:

    出现错误是因为语言中存在一个约束(在 F2008 中,语法规则为 C618),即结构组件(或类似的多部分参考)中只有一个部分可能具有非零等级。您对结构组件的引用 bararray%vector 包含两个非零等级的部分 - 组件 vector 和变量 bararray

    (在“更改调用”方法中,对分量向量的引用有一个下标,该下标使该部分的总体排名为零,因此接受 bararray%vector(1)。)

    “改变调用”方法存在一个重大的潜在问题。

    子程序中的哑元是INTENT(INOUT)。这要求实际参数是可定义的(一个实际上可以“变化”的变量)。

    在您的“更改调用”方法中,与该虚拟参数关联的实际参数是一个表达式 - 一个数组构造函数。表达式是不可定义的——评估它们的结果是一个值,而不是可以“定义”的东西——即你不能理智地说2 + 2 = 6

    大概在实际代码中,doSomething 子例程是一个外部过程,因此您的编译器没有诊断出这一点。如果 doSomething 有一个显式接口(可能是因为它在一个模块中),那么我希望编译器会报告一个错误。

    其他人建议在调用之前重新编组数据的方法(将数据复制到适当大小的数组变量中,调用过程,将数据复制出来)。重写子例程的接口以获取 bar 类型的对象(将类型的定义移至模块)显然是另一种可能性。派生类型不仅是存储数据的便捷方式,而且通常是处理数据的便捷方式。

    我会非常警惕那些试图诱使处理器接受你“知道”为内存中假设数组bararray%vector 的布局的方法。类型和数组的布局可能因处理器而异,而且随着处理器错误检查的改进,这种技巧可能会导致以后的诊断。供应商提供的库可以摆脱这种伎俩,但我们这些普通的程序员可不行。

    【讨论】:

    • 2+2=6 in Z2 :P 更严肃地感谢您的回答,您很好地解释了为什么它不起作用以及为什么我不应该使用 (/v1,v2/ ) 方法。
    【解决方案2】:

    Intel Fortran (13.0.xxx) 编译器报错

    error #6159: A component cannot be an array if the encompassing structure is an array.
    

    我认为你的问题的答案是调用带有real, dimension(3,2) 参数的子例程的正确方法是给该死的东西一个real, dimension(3,2) 参数,这需要在之前以某种方式编组你的数据调用子程序。是的,这有点麻烦,但是严格的类型检查不是一个很好的功能吗?

    您可以按照以下方式定义包装子例程:

    subroutine wrapDoSomething(abar)
      type(bar), dimension(:), intent(inout) :: abar
        ...
        ... marshal your data ...
        call subroutine doSomething(marshalled_data)
        ...
    end subroutine wrapDoSomething
    

    我怀疑,如果您在没有显式接口的情况下定义 subroutine doSomething,您可能会通过传递调用者认为是 bars 的数组但子例程得到的是 real 的 rank-2 数组而侥幸逃脱s。如果这不能直接在块外工作,则可能在您的类型定义中使用BIND(C),这可能会欺骗编译器为 6 个实数分配连续存储。

    如果这一切都在你的脸上爆炸,你就靠你自己了,我将被拒绝参与该任务。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-18
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多