1

我手头有以下程序

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)/))

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

提前致谢。

4

2 回答 2

6

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

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

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

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

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

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

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

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

于 2012-08-15T21:19:19.140 回答
5

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 的数组但子例程作为 s 的 rank-2 数组得到的东西real。如果这不能直接从块中使用,则可能BIND(C)在您的类型定义中使用,这可能会欺骗编译器为 6 个实数分配连续存储。

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

于 2012-08-15T10:39:17.107 回答