目前尚不清楚是否确实回答了 OP 的部分问题。此外,在随后的答案/讨论中似乎有很多混乱和各种错误,可能会受益于一些澄清。
A)OP的问题Re
“那么我也想知道为什么要使用intent(out),因为intent(inout) 需要的工作更少(无需复制数据)。”
可能没有回答,或者至少太直接/正确。
首先,要明确Intent
属性至少有两个目的:“安全/卫生”和“间接性能”问题(不是“直接性能”问题)。
1) 安全/卫生:协助生成“安全/合理”的代码,减少“搞砸”的机会。因此,Intent(In) 不能被覆盖(至少在本地,甚至在某些情况下“全局”,见下文)。
同样,Intent(Out) 要求为 Arg 分配“明确答案”,从而有助于减少“垃圾”结果。
例如,在可能是计算数学中最常见的问题的解决方案中,即所谓的“Ax=b 问题”,人们正在寻找的“直接结果/答案”是向量 x 的值。那些应该是 Intent(Out) 以确保 x 被分配一个“明确”的答案。如果 x 被声明为 Intent(InOut) 或“no Intent”,那么 Fortran 将为 x 分配一些“默认值”(在调试模式下可能是“零”,但在发布模式下可能是“垃圾”,无论是内存在 Args 指针位置),如果用户没有明确地为 x 分配正确的值,它将返回“垃圾”。Intent(Out) 将“提醒/强制”用户明确地为 x 赋值,从而避免这种“(意外)垃圾”。
在求解过程中,将(几乎肯定)产生矩阵 A 的逆矩阵。用户可能希望将该逆矩阵返回给调用 s/r 来代替 A,在这种情况下,A 应该是 Intent(InOut)。
或者,用户可能希望确保不对矩阵 A 或向量 b 进行任何更改,在这种情况下,它们将被声明为 Intent(In),从而确保不会覆盖关键值。
2 a) “间接绩效”(和“全球安全/卫生”):虽然意图不是直接影响绩效,但它们是间接影响的。值得注意的是,某些类型的优化,尤其是 Fortran Pure 和 Elemental 构造,可以大大提高性能。这些设置通常要求所有 Args 都明确声明其 Intent。
粗略地说,如果编译器事先知道所有 var 的 Intent,那么它可以更轻松有效地优化和“愚蠢检查”代码。
至关重要的是,如果一个人使用 Pure etc 构造,那么很有可能还会有一种“全局安全/卫生”,因为 Pure/Elemental s/p 只能调用其他 Pure/Elemental s/p 等等无法达到“格雷泽盖伊”示例中所示的那种情况。
例如,如果 Sub1() 声明为 Pure,那么 Sub2() 也必须声明为 Pure,然后需要声明所有级别的 Intent,因此“The Glazer Guy”中产生的“garbage out” “示例不可能发生。也就是说,代码将是:
Pure subroutine sub_P(i)
integer,intent(in) :: i
call sub2_P(i)
end subroutine sub_P
Pure subroutine sub2_P(i)
implicit none
! integer i ! not permitted to omit Intent in a Pure s/p
integer,intent(in) :: i
i = 7 ! This WILL NOT WORK/HAPPEN, since Pure obviates the possibility of omitting Intent, and Intent(In) prohibits assignment ... so "i" remains "safe".
end subroutine sub2_P
...在编译时,这会产生类似
“ ||错误:在 (1)| 的变量定义上下文(赋值)中带有 INTENT(IN) 的虚拟参数 'i'|”
当然,sub2 不必是 Pure 才能将 i 声明为 Intent(In),这将再次提供人们正在寻找的“安全/卫生”。
请注意,即使 i 被声明为 Intent(InOut),它仍然会因 Pure 失败。那是:
Pure subroutine sub_P(i)
integer,intent(in) :: i
call sub2_P(i)
end subroutine sub_P
Pure subroutine sub2_P(i)
implicit none
integer,intent(inOut) :: i
i = 7 ! This WILL NOT WORK, since Pure obviates the possibility of "mixing" Intent's.
end subroutine sub2_P
...在编译时,这会产生类似
“||错误:变量定义上下文中带有INTENT(IN)的虚拟参数'i'(INTENT的实际参数= OUT / INOUT)在(1)|”
因此,对纯/元素构造的严格或广泛依赖将确保(大部分)“全球安全/卫生”。
不可能在所有情况下都使用 Pure/Elemental 等(例如,许多混合语言设置,或者依赖于您无法控制的外部库等)。
尽管如此,始终如一地使用 Intents,并尽可能使用 Pure 等,将产生很多好处,并消除很多悲伤。
人们可以简单地养成在可能的情况下始终在任何地方声明意图的习惯,无论是否纯正......这是推荐的编码实践。
...这也凸显了同时存在 Intent(InOut) 和 Intent(Out) 的另一个原因,因为 Pure 必须声明所有 Arg 的 Intent,因此会有一些 Arg 仅是 Out,而另一些是 InOut (即,如果没有 In、InOut 和 Out Intent 中的每一个,就很难拥有 Pure's)。
2 b) OP 的评论期望“性能改进”因为不需要复制“表明对 Fortran 及其广泛使用按引用传递的误解。通过引用传递,本质上,只需要指针,实际上,通常只需要需要指向数组中第一个元素的指针(加上一些隐藏的数组信息)。
事实上,考虑“过去”(例如 Fortran IV、77 等)可能会提供一些见解,当传递数组时可能已编码如下:
Real*8 A(1000)
Call Sub(A)
Subroutine Sub(A)
Real*8 A(1) ! this was workable since Fortran only passes the pointer/by ref to the first element of A(1000)
! modern Fortran may well throw a bounds check warning
在现代 Fortran 中,“等效”是在 s/r 中将 A 声明为 Real(DP) A(:) (尽管严格来说,有各种设置受益于传递数组的边界并明确声明边界,但是将是另一天冗长的题外话)。
也就是说,Fortran 不会按值传递,也不会为 Args/Dummy 变量“制作副本”。调用 s/r 中的 A() 与 s/r 中使用的“相同 A”(当然,在 s/r 中,可以复制 A() 或其他任何内容,这将创建额外的工作/空间要求,但这是另一回事)。
主要是因为这个原因,Intent 不会在很大程度上直接影响性能,即使对于大型数组 Arg 等也是如此。
B)关于“按值传递”的混淆:尽管上面的各种响应确实证实使用 Intent 是“不按值传递”,但澄清此事可能会有所帮助。
将措辞更改为“意图始终通过引用传递”可能会有所帮助。这与“不按值传递”不同,是一个重要的微妙之处。值得注意的是,Intent 不仅是“byRef”,而且 Intent 可以防止按值传递。
尽管有一些特殊的/更复杂的设置(例如混合语言 Fortran DLL 等)需要更多的讨论,但对于“标准 Fortran”的大部分内容,Args 由 Ref 传递。这种“意图微妙”的演示可以在“The Glazer Guys”示例的简单扩展中看到,如下所示:
subroutine sub(i)
integer, intent(in) :: i, j
integer, value :: iV, jV
call sub2(i)
call sub3(i, j, jV, iV)
end
subroutine sub2(i)
implicit none
integer i
i = 7 ! This works since the "intent" information was lost.
end
subroutine sub3(i, j, jV, iV)
implicit none
integer, value, Intent(In) :: i ! This will work, since passed in byRef, but used locally as byVal
integer, value, Intent(InOut) :: j ! This will FAIL, since ByVal/ByRef collision with calling s/r,
! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
integer, value, Intent(InOut) :: iV ! This will FAIL, since ByVal/ByRef collision with calling s/r,
! ... in spite of "byVal" in calling s/r
! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
integer, value, Intent(Out) :: jV ! This will FAIL, since ByVal/ByRef collision with calling s/r
! ... in spite of "byVal" in calling s/r
! ||Error: VALUE attribute conflicts with INTENT(OUT) attribute at (1)|
jV = -7
iV = 7
end
也就是说,任何具有“Out”方面的东西都必须是“byRef”(至少在正常设置中),因为调用 s/r 期待“byRef”。因此,即使所有 s/r 都将 Args 声明为“Value”,它们也仅在本地是“byVal”(同样在标准设置中)。因此,被调用的 s/r 尝试返回一个声明为具有任何 Out Intent 的值的 Arg 的任何尝试都将由于传递样式的“冲突”而失败。
如果它必须是“Out”或“InOut”和“Value”,那么就不能使用Intent:这不仅仅是说“它不是按值传递”。