Fortran中的intent(inout)与省略intent的区别

39

良好的惯例规定,Fortran中的子程序参数应该每个都有一个指定的意图(即使用 intent(in)intent(out)intent(inout),详见此问题):

subroutine bar (a, b)
    real, intent(in) :: a
    real, intent(inout) :: b
    b = b + a
    ...

然而,不指定意图也是合法的Fortran:

subroutine bar (a, b)
    real, intent(in) :: a
    real :: b
    b = b + a
    ...

除了编译时检查,指定为 intent(inout) 的参数和未指定 intent 的参数之间是否有任何真正的区别?如果我要给老的、没有指定 intent 的代码添加意图,是否需要担心什么?

4个回答

29
根据亚当斯等人的《Fortran 2003手册》,intent(inout)参数和未指定意图的参数之间有一个区别。 在intent(inout)情况下,实际参数(即在调用者中)必须始终是可定义的。 如果未指定意图,则如果子例程尝试定义虚拟参数,则该参数必须是可定义的。 "可定义"是指设置值:dummy_arg = 2.0。显然,如果这样做,实际参数应该是一个变量。对于intent(inout),无论子程序是否执行此操作,实际参数都必须是可定义的。如果没有指定意图,则取决于该子程序调用的情况-如果子程序不定义变量,则没有问题;如果它这样做,那么就有问题-例如,向常量作为实际参数写入显然会引起问题。
这并不意味着编译器将诊断所有这些情况-编译器需要诊断的标准是不同的问题。在编译时检测意图未指定的错误几乎不可能,因为违规行为取决于代码的运行时流程。对于intent(inout)情况,编译器更容易诊断并警告您代码中的问题。

4
好的!您能否举一个子程序的例子来解释它们之间的差异? - jvriesem

7
您的问题让我想到(现在有很多事情要做),如果您的代码将参数作为实际参数传递,然后子程序尝试写入该参数,您可能会遇到行为差异。 没有INTENT声明,编译器可能会让这个问题继续存在,导致奇怪的行为。有了声明,我期望会出现编译时错误。
您和我可能认为INOUT和没有INTENT声明之间没有区别,但不要忘记有很多旧的Fortran程序,与旧语言版本兼容是新标准的重要特性。如果它是正确的(但危险的)FORTRAN77,则许多人希望他们的代码仍然可以使用Fortran 90+编译器(虽然仍然危险)。
2003年标准的快速阅读确实表明INOUT和没有INTENT之间有区别,但需要更仔细地阅读。如果您测试了此功能,请告诉我们您的结论;如果我稍后有时间,我会自己测试并告诉您。

5
我构建了四个测试用例,试图修改子程序中的参数。两个测试使用了独立的外部子程序--即它们在单独的文件中--一个有intent(inout)声明,一个没有。编译器对它们都没有投诉,这并不奇怪(没有接口),但结果的exe会发生段错误。我将子程序放入一个模块中进行了最后两个测试,看到了无意向声明的segfault以及一个编译器错误("实际参数在(1)处必须可以定义,因为虚拟参数“b”的INTENT=OUT/INOUT")与接口一起使用。这是使用来自debian系统的gfortran 4.4.4-2的情况。 - Andrew Walker
2
现如今,参数被放置在内存的只读标记部分,但这并不总是正确的。在很早期的编译器中一个著名的例子就是重新定义数字常量。http://coding.derkeiler.com/Archive/Fortran/comp.lang.fortran/2005-01/0485.html - Vladimir F Героям слава

6
要了解意图在内/外的作用,您需要知道Fortran 实际上通过引用传递变量。但这并不总是与实际传递引用相同。
如果您将2-D数组的内部子部分(即:data(i1:i2, j1:j2))传递给子例程,则Fortran会将该数据复制到一个连续的内存段中,并将新地址传递给例程。返回时,数据将被复制回其原始位置。
通过指定INTENT,编译器可以跳过其中一个复制操作。
它不仅充当修改要保留不变的数据的安全保障,而且在处理大型数据集时还可以加快代码速度。

1
为了更好地解释M.S.B.的答案,省略intent可以提供更多的灵活性,但代价是减少编译时检查。没有intent,如果你知道代码分支不会尝试修改它,你可以传递一个字面常量;而使用intent(inout),如果你尝试这样做,编译器可能会警告你。这在“双重”过程中可能很有用,这些过程将根据选项修改或不修改某个参数,例如:
subroutine copy(x,a,readwrite)
integer :: x, a
integer, intent(in) :: readwrite

if (readwrite == 0) then
  x = a
else
  a = x
end if

end subroutine

如果您想将 intent 添加到 xa 中,它们两个都必须是 inout,因为它们都可能被读取和修改。但这不允许您编写例如:
call copy(x,3,0)  ! equiv. to x=3
call copy(42,a,1) ! equiv. to a=42

这个例子有些愚蠢,但可以想象一个更复杂的子程序,它可以读取或写入带有一些复杂格式的文件。(我并不是说这是最好的解决方案,但这是你可以轻松找到的东西。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接