【问题标题】:Fortran intent(inout) versus omitting intentFortran 意图(inout)与省略意图
【发布时间】:2011-02-22 06:12:27
【问题描述】:

良好的实践要求 Fortran 中的每个子例程参数都应具有指定的意图(即 intent(in)intent(out)intent(inout),如 this question 所述):

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) 的参数和没有指定意图的参数之外,是否有任何真正的区别?如果我将意图改造为旧的、无意图的代码,我应该担心什么?

【问题讨论】:

    标签: fortran fortran90


    【解决方案1】:

    根据 Adams 等人的 The Fortran 2003 手册,intent(inout) 参数和没有指定意图的参数之间存在一个区别。 intent(inout) 案例中的实际参数(即调用者中的参数)必须始终是可定义的。如果未指定意图,则参数必须是可定义的如果子例程的执行尝试定义虚拟参数。 可定义 表示设置值:dummy_arg = 2.0。显然,如果这样做,实际参数应该是一个变量。对于意图(输入输出),无论子例程是否执行此操作,实际参数都必须是可定义的。如果没有指定意图,它取决于子例程的特定调用发生了什么——如果子例程没有定义变量,那没关系;如果是这样,那就有问题了——比如写入一个常量的实际参数显然会导致问题。

    这并不意味着编译器会诊断所有这些情况——标准要求编译器诊断的是不同的问题。在编译时检测意图未指定案例要求的所有错误几乎是不可能的,因为违规取决于代码的运行时流程。编译器更容易诊断意图(输入)情况并警告您代码问题。

    【讨论】:

    • 整洁!您是否愿意/能够使用示例子程序来解释差异?
    【解决方案2】:

    您的问题让我想知道(现在有很多事情要做)如果您的代码将 PARAMETER 作为实际参数传递给您的子程序然后尝试写入,您是否会遇到行为差异。如果没有 INTENT 声明,编译器可能会放手,导致奇怪的行为。有了声明,我预计会出现编译时错误。

    你和我可能认为 INOUT 和没有 INTENT 声明之间没有区别,但不要忘记那里有很多旧的 Fortran 程序,与旧语言版本的兼容性是新标准的重要特征.如果 FORTRAN77 正确(但不可靠),那么很多人希望他们的代码使用 Fortran 90+ 编译器保持正确(仍然不可靠)。

    快速阅读 2003 标准确实表明 INOUT 和无 INTENT 之间存在差异,但需要更仔细地阅读。如果您对此进行测试,请告诉我们您的结论;如果以后有时间我会自己测试并告诉你。

    【讨论】:

    • 我已经构建了四个测试用例,试图修改子程序中的参数。两个带有外部子程序的测试 - 即在一个文件中 - 有和没有意图(inout)。编译器不会抱怨任何这些。这并不奇怪(没有接口)和由此产生的 exe 段错误。我已将子例程卡在模块中以进行最后两个测试,并查看无意图情况的段错误和编译器错误(“必须将 (1) 处的实际参数定义为虚拟参数 'b' 是 INTENT = OUT/ INOUT") 与接口。这是来自 debian 系统的 gfortran 4.4.4-2。
    • 这些天的参数被放置在内存的只读标记部分中,但这并不总是如此。非常古老的编译器中著名的例子是重新定义数字常量coding.derkeiler.com/Archive/Fortran/comp.lang.fortran/2005-01/…
    【解决方案3】:

    要了解意图输入/输出的作用,您需要知道在内部,Fortran 有效地通过引用传递变量。这并不总是与实际通过引用传递相同。

    如果您将二维数组的内部子段传递给子例程,即:data(i1:i2, j1:j2),则 Fortran 将该数据复制到内存的连续段中,并将新地址传递给例程。返回后,数据将被复制回其原始位置。

    通过指定INTENT,编译器可以知道跳过其中一个复制操作。

    它不仅可以作为修改您希望保持不变的数据的故障保护,还可以在处理大型数据集时加速您的代码。

    【讨论】:

      【解决方案4】:

      为了以某种方式扩展 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
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-09
        • 1970-01-01
        • 1970-01-01
        • 2013-01-29
        相关资源
        最近更新 更多