我正在浏览一个 Fortran 代码,有一点让我有点困惑。
有一个子程序,比如说
SUBROUTINE SSUB(X,...)
REAL*8 X(0:N1,1:N2,0:N3-1),...
...
RETURN
END
在另一个子例程中通过以下方式调用:
CALL SSUB(W(0,1,0,1),...)
其中 W 是一个“工作数组”。似乎 W 中的特定值被传递给 X,但是,X 被标注为数组。这是怎么回事?
我正在浏览一个 Fortran 代码,有一点让我有点困惑。
有一个子程序,比如说
SUBROUTINE SSUB(X,...)
REAL*8 X(0:N1,1:N2,0:N3-1),...
...
RETURN
END
在另一个子例程中通过以下方式调用:
CALL SSUB(W(0,1,0,1),...)
其中 W 是一个“工作数组”。似乎 W 中的特定值被传递给 X,但是,X 被标注为数组。这是怎么回事?
这是让子例程在原始数组的(N 维中的矩形)子集上工作的不常见的习惯用法。
Fortran 中的所有参数(至少在 Fortran 90 之前)都是通过引用传递的,因此实际的数组参数被解析为内存中的一个位置。在为整个数组分配的空间内选择一个位置,子程序只操作数组的一部分。
最大的问题:您必须了解数组在内存中的布局方式以及 Fortran 的数组索引方案是如何工作的。Fortran 使用列主数组排序,这与 c 的约定相反。考虑一个大小为 5x5 的数组(并从 0 开始对两个方向进行索引,以使与 c 的比较更容易)。在这两种语言中,0,0 是内存中的第一个元素。在 c中,内存中的下一个元素是[0][1]
,但在 Fortran 中是(1,0)
. 这会影响您在选择子空间时删除哪些索引:如果原始数组是 A(i,j,k,l),并且子例程适用于三维子空间(如您的示例中),则在 c 中它适用于Aprime[i=constant][j][k][l]
,但是在 Fortran 中工作于Aprime(i,j,k,l=constant)
.
另一个风险是环绕。子程序中(子)数组的尺寸必须与调用程序中的尺寸相匹配,否则会发生奇怪,奇怪的事情(想想)。因此,如果 A 被声明为大小 (0:4,0:5,0:6,0:7),并且我们使用 element 调用A(0,1,0,1)
,则接收例程可以自由地在它喜欢的任何位置开始每个维度的索引,但必须制作尺寸(4,5,6)
或其他;但这意味着 j 方向上的最后一个元素实际上是环绕的!对此要做的事情是不使用最后一个元素。确保发生这种情况是程序员的工作,而且是一件很痛苦的事情。小心。多多关照。
这称为“序列关联”。在这种情况下,似乎是一个缩放器,一个数组的元素(调用者中的实际参数)与一个数组(隐含的第一个元素)相关联,即子例程中的虚拟参数。此后,数组的元素通过存储顺序关联,称为“序列”。由于各种原因,这是在 Fortran 77 及更早版本中完成的,这里显然是为了工作区数组——也许程序员正在做他们自己的内存管理。这保留在 Fortran >=90 中以实现向后兼容性,但 IMO 不属于新代码。
在 fortran 中,变量是按地址传递的。W(0,1,0,1)
价值和地址也是如此。所以基本上你从 . 开始传递子数组W(0,1,0,1)
。