在下面的代码中,我声明了一个包含20个元素的数组mass
。传递给子例程foo
时,foo
被告知mass
只有10个元素。但是,我仍然可以访问第20个元素。我的问题是:
为什么我可以将数组传递给子程序并告诉子程序数组的大小错误?
为什么我仍然可以访问第20个元素,即使子例程认为它只有10个元素?
我在foo
中对数组中的第20个元素所做的任何更改是否与数组保持一致,就像foo
知道数组的正确大小一样?
以下是代码:
program test
implicit none
integer num2,i
real*8 mass(20)
num2=10
do i = 1,num
mass(i) = 1.d0
end do
call foo(num2,mass)
end
subroutine foo(num2,mass)
integer num2
real*8 mass(num2)
write(*,"(A20,E15.9)") "first one:",mass(1)
write(*,"(A20,E15.9)") "tenth one:",mass(10)
write(*,"(A20,E15.9)") "twentieth one:",mass(20)
continue
end
注意:这个子例程被告知数组大小错误的情况是我在别人的代码中遇到的那个我试图修改我自己的用法。
答案 0 :(得分:4)
为什么我可以将数组传递给子程序并告诉子程序数组的大小错误? 因为Fortran允许您使用数组的一部分。
为什么我仍然可以访问第20个元素,即使子例程认为它只有10个元素?
因为1)数组元素在内存中是连续的,所以索引到达第20个元素,2)Fortran生成的程序通常不会检查编程是否正在执行无效的数组下标。大多数编译器都可以选择插入此类检查,例如,-fcheck=bounds
或-fcheck=all
用于gfortran。
我在foo中对数组中的第20个元素所做的任何更改是否与数组保持一致,就像foo知道数组的正确大小一样? 是。如果你这样做并且原始数组没有20个元素,你会改变一个不相关的内存位置,结果不好。
答案 1 :(得分:4)
你的问题背后有一些误解,我将在这个答案中提到。 answer by M. S. B.中给出了一些实际含义。
你说你声明了一个包含20个元素的数组mass
,但是foo
被告知它只有10个元素。这是不正确的。
你拥有的实际上是两个不同的实体:主程序中的数组mass
,包含20个元素,以及子例程{{1}中大小为10的数组(也称为mass
) }。
“传递”是在这两个实体之间建立关联(所谓的参数关联)。主程序中名为foo
的数组是子程序中的实际参数,子程序中名为mass
的数组是伪参数。
伪参数是一个显式形状的数组,范围mass
(也是与主程序的num2
相关联的参数)。伪参数的第一个num2
元素与实际参数的第一个num2
元素相关联。 [这导致要求主程序中的数组中至少有num2
个元素。]
那么,第一个问题的答案
为什么我可以将数组传递给子程序并告诉子程序数组的大小错误?
就是这样:你没有告诉它错误的大小,你只是说子例程中的数组对应于参数的第一个num2
元素。
来到
为什么我仍然可以访问第20个元素,即使子例程认为它只有10个元素?
这是编程错误。不允许使用大于数组范围的下标值。尝试此操作时会发生什么取决于编译器。正如另一个答案所说,很可能这只是访问(因为实现了关联的方式)内存中与实际参数的大元素相对应的位置。但同样,编译器可能会抱怨(特别是那些检查选择的编译选项),或者如果传递是通过临时复制完成的,那么事情可能会崩溃。 [后者不太可能。]
最后
我在foo中对数组中的第20个元素所做的任何更改是否与数组保持相同,就好像foo知道数组的正确大小一样?
这又是特定于实现的。没有正确的Fortran答案,因为您的程序不符合要求。与前一点一样,如果这是一个与实际参数的相应元素相对应的内存区域,并且没有边界检查,则更改可能会持续存在。如果编译器选择执行复制,则可能会发生崩溃,或者返回时可能会忽略边界外的更改。 [同样,最后两个不太可能,因为它需要编译器“聪明”。]