类型数组内的数组作为函数参数

时间:2012-08-15 10:06:00

标签: fortran argument-passing fortran90 gfortran

我手边有以下程序

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

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

提前致谢。

2 个答案:

答案 0 :(得分:6)

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

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

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

子程序中的伪参数是INTENT(INOUT)。这需要实际的论证是可定义的(一个实际上可以“改变”的变量)。

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

据推测,在实际代码中,doSomething子例程是一个外部过程,因此您的编译器尚未对此进行诊断。如果doSomething有一个显式接口(因为它可能在一个模块中),那么我希望编译器报告错误。

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

我会非常警惕尝试欺骗处理器接受你“知道”内存中假设数组bararray%vector的布局的方法。类型和数组的布局可以在处理器之间变化,加上处理器错误检查可以改善这种技巧,可能导致以后的诊断。供应商提供的库可以摆脱这种伎俩,但不仅仅是编程凡人。

答案 1 :(得分: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,你可能会放弃传递它,调用者认为是bar的数组,但子程序作为rank-2数组获得的是什么real的。{如果这不能直接使用块,可能在类型定义上使用BIND(C),这可能会欺骗编译器为6个实数分配连续存储。

如果这一切都在你的脸上爆炸,你就是你自己,我将不会参与这项任务。