Fortran中展示intent(out)和intent(inout)的有效程序的差异

3
这是对我在stackoverflow上发现的一篇文章的跟进:Difference between intent(out) and intent(inout)

链接中的问题询问Fortran中intent(out)intent(inout)之间的区别,通过提出一个无效程序
是否有人可以想出一个简单的有效程序来,通过将intent(inout)更改为intent(out)或反之亦然来给出不同的结果?
2个回答

5

给你...

program intent_test
implicit none
integer, allocatable :: a(:)

a = [1,2,3,4,5]

call intent_inout (a)

call intent_out (a)

contains

subroutine intent_inout (a)
integer, allocatable, intent(inout) :: a(:)

if (allocated(a)) then
  print *, a
else
  print *, "Unallocated"
end if

end subroutine intent_inout

subroutine intent_out (a)
integer, allocatable, intent(out) :: a(:)

if (allocated(a)) then
  print *, a
else
  print *, "Unallocated"
end if

end subroutine intent_out

end program intent_test

1 2 3 4 5 未分配的空间


1
+1。感谢您的快速回复,史蒂夫!在这个例子中使用了一个数组,而@francescalus使用“type”来完成“技巧”。这让我想知道,如果一个人只使用“integer”变量和仅执行简单算术或仅使用“print”函数的子程序,那么“out”和“inout”是否总是会给出相同的结果。(当然,这将是另一个问题。)我怀疑这是不可能的。但是在尝试了一些例子后,我没有发现任何区别。 - user758077
1
重要的是属性ALLOCATABLE - 变量可以是任何类型。按照标准,INTENT(OUT)意味着虚拟参数的定义状态在进入过程时是“未定义”的,但在其他情况下很难检测。关于@francescalus使用“disassociates”这点,让我感到不安。指针会被取消关联,而INTENT(OUT)会导致指针未定义,而不是解除关联,因此您不能假设或甚至测试指针是否已解除关联。 - Steve Lionel
是的,我在使用“disassociate”作为“成为未定义关联状态”的(懒惰的)缩写(请参见我的答案以获得更精确的状态说明)。请注意,“指针变为未定义”也同样不精确:它是未定义的(这没问题),但它的关联状态也是未定义的(这不好)。 - francescalus

3
作为Steve Lionel的答案,以及我原始回答中的评论,这些或许激发了你的兴趣,intent(out)对于虚拟参数(以及实际参数)初始状态的影响是回答这个问题的一种方式。 intent(inout)使虚拟参数反映进入过程时实际参数的值。 intent(out)“重置”虚拟参数。在链接的问题中,这种“未定义”是程序无效的原因。
更准确地说,我们可以关于intent(out)虚拟参数说以下事情:
  • 可分配实际参数将被取消分配;
  • 指针实际参数的指针关联变得未定义;
  • 对于非指针虚拟参数,任何未默认初始化的组件都变得未定义。
链接的问题因尝试引用这样一个新定义的值而违反了第三点。然而,默认初始化的组件不是未定义的,这导致我们获得了第一类有效程序:
  implicit none
  type t
     integer :: x=1
  end type t
  type(t) :: x=t(0)

  call s1(x)
  call s2(x)

contains
  subroutine s1(x)
    type(t), intent(inout) :: x
    print*, x%x
  end subroutine s1

  subroutine s2(x)
    type(t), intent(out) :: x
    print*, x%x
  end subroutine s2

end

重要的是,组件的默认初始化意味着即使在进入s2时,x%x也不是未定义的,但它可能与过程输入之前的实际参数组件的值不同。
使用指针参数编写合适的程序很棘手:具有未定义指针关联状态的指针无法在重新定义其指针关联状态之前被引用/查询。
这让我们看到了可分配组件。与指针不同,在该指针关联状态未定义时,我们无法查询指针关联状态,但我们可以询问分配状态。与intent(out)虚参对应的实参已被解除分配;对于intent(inout),分配状态保持不变:
  implicit none
  integer, allocatable :: i
  allocate (i)

  call s1(i); print*, allocated(i)
  call s2(i); print*, allocated(i)

contains

  subroutine s1(i)
    integer, allocatable, intent(inout) :: i
  end subroutine s1

  subroutine s2(i)
    integer, allocatable, intent(out) :: i
  end subroutine s2
end

(这是 Steve Lionel 示例的简化版本。)

所有这些都表明存在差异。不正确使用 intent 可能导致无效程序或意义上的重大变化。理解 intent(in)intent(inout)intent(out) 和未指定 intent 意味着成为 Fortran 程序员的关键部分。


谢谢!在我看来,“intent(inout)”和“intent(out)”之间的区别非常微妙,尽管它们的基本机制不同,但它们产生相同的结果并不罕见。 - user758077
需要记住的重要事情是,INTENT 实际上是为程序员提供帮助的 - 它提供了编译器可以用来警告可能出现的错误的额外信息。例如,将常量传递给 INTENT(INOUT) 参数是一个错误,因为 INTENT(INOUT) 要求实际参数可定义。这与省略 INTENT 是不同的。(我支持 INTENT(NONE) 与省略 INTENT 的含义相同。)使用 INTENT(IN),编译器可以在实际未定义时警告您。 - Steve Lionel
我仍然有点困惑,是否正确的做法是在两种情况下(inout或out)我仍然需要在主程序中声明变量?如果子程序内没有变量声明,则其维度可能与主程序中的等效维度不同吗? - Herman Toothrot
@HermanToothrot,我不确定我理解你的问题。但是,让我们看看这是否有所帮助:使用intent(inout)intent(out)时,在引用过程的地方(比如主程序)必须有一个相应的(可定义的对象/)变量。对于这些意图,不能将表达式用作参数。数组维度是否匹配与任何指定的意图无关。(请注意,这里的示例中没有数组。) - francescalus

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