在书籍中搜索了一段时间后,在stackoverflow和一般网络上,我发现很难找到对fortran参数意图之间真正差异的直接解释。我理解它的方式是:
intent(in)
- 将实际参数复制到条目处的伪参数。intent(out)
- 伪参数指向实际参数(它们都指向内存中的相同位置)。intent(inout)
- 伪参数在本地创建,然后在过程完成时复制到实际参数。如果我的理解是正确的,那么我也想知道为什么人们想要使用intent(out)
,因为intent(inout)
需要更少的工作(不复制数据)。
答案 0 :(得分:20)
意图只是编译器的提示,你可以抛弃那些信息并违反它。意图几乎完全存在,以确保您只执行您计划在子例程中执行的操作。编译器可能会选择信任您并优化某些内容。
这意味着intent(in)
没有按值传递。您仍然可以覆盖原始值。
program xxxx
integer i
i = 9
call sub(i)
print*,i ! will print 7 on all compilers I checked
end
subroutine sub(i)
integer,intent(in) :: i
call sub2(i)
end
subroutine sub2(i)
implicit none
integer i
i = 7 ! This works since the "intent" information was lost.
end
program xxxx
integer i
i = 9
call sub(i)
end
subroutine sub(i)
integer,intent(out) :: i
call sub2(i)
end
subroutine sub2(i)
implicit none
integer i
print*,i ! will print 9 on all compilers I checked, even though intent was "out" above.
end
答案 1 :(得分:6)
intent(in)
- 看起来像传递值(并且它的更改不会反映在外部代码中),但实际上是通过引用传递,并且编译器禁止更改它。但它仍然可以改变。intent(out)
- 以某种方式通过引用传递,实际上是一个返回参数intent(inout)
- 通过引用传递,正常输入/输出参数。如果显而易见,请使用intent(out)
来记录您的设计。如果有的话,不要关心很少的性能提升。 (评论表明没有,因为intent(in)
在技术上也通过引用传递。)
答案 2 :(得分:1)
目前尚不清楚OP部分问题是否得到了实际回答。此外,当然在随后的答案/讨论中似乎存在许多混淆和各种错误,可能会从某些澄清中受益。
A)OP的问题Re
"然后我也想知道为什么人们想要使用意图(out),因为意图(inout)需要更少的工作(不复制数据)。"
可能没有回答,或者至少太直接/正确。
首先,要明确Intent
属性至少有两个目的:"安全/卫生"和"间接性能"问题(不是"直接表现"问题)。
1)安全/卫生:协助生产"安全/合理"代码减少了把事情搞乱的机会"起来。因此,Intent(In)不能被覆盖(至少在本地,甚至"全局"在某些情况下,见下文)。
同样,Intent(Out)要求为Arg分配一个明确的答案",从而有助于减少"垃圾"结果
例如,在解决计算数学中最常见的问题,即所谓的" Ax = b问题","直接结果/答案"一个是寻找矢量x的值。那些应该是Intent(Out)以确保x被分配一个" explicit"回答。如果x被声明为,例如,Intent(InOut)或"没有Intent",那么Fortran将为x分配一些"默认值" (可能"零"在调试模式下,但可能"垃圾"在发布模式下,是Args指针位置的内存中的任何内容),如果用户没有然后明确地将正确的值分配给x,它将返回"垃圾"。意图(Out)将提醒/强制"用户明确地将值赋给x,从而避免了这种"(偶然的)垃圾"。
在求解过程中,人们(几乎可以肯定地)产生矩阵A的逆矩阵。用户可能希望将该逆转换回调用s / r代替A,在这种情况下A应该是Intent(InOut) 。
或者,用户可能希望确保不对矩阵A或向量b进行任何更改,在这种情况下,它们将被声明为Intent(In),从而确保不会覆盖关键值。
2 a)"间接表现" (以及"全球安全/卫生"):尽管意图并非直接影响绩效,但它们间接地这样做。值得注意的是,某些类型的优化,特别是Fortran Pure和Elemental构造,可以产生大大改进的性能。这些设置通常要求所有Args明确声明其Intent。
粗略地说,如果编译器事先知道所有变量的意图,那么它可以优化和"愚蠢检查"代码更容易和有效。
至关重要的是,如果一个人使用Pure等结构,那么,很有可能会出现一种全球安全/卫生的问题。同样,由于Pure / Elemental s / p只能打电话给其他Pure / Elemental s / p,所以一个人无法到达" Glazer Guy&#39所示的那种情况。 ; S"例。
例如,如果Sub1()被声明为Pure,那么Sub2()也必须声明为Pure,然后它将被要求在所有级别声明Intents,因此"垃圾输出&# 34;制作于" The Glazer Guy"例子不可能发生。也就是说,代码将是:
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) "
当然,sub2不需要Pure就可以将我声明为Intent(In),这将再次提供"安全/卫生"一个人正在寻找。
请注意,即使我被宣布为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)中的变量定义上下文(实际参数为INTENT = OUT / INOUT)at(1)|"
因此,严格或广泛依赖Pure / Elemental构造将确保(大部分)全球安全/卫生"。
在所有情况下都不可能使用Pure / Elemental等(例如,许多混合语言设置,或依赖于您无法控制的外部库等)。
尽管如此,一直使用Intents,并且只要有可能Pure等,都会产生很多好处,并消除很多悲伤。
人们可以简单地养成在可能的情况下随时随地声明意图的习惯,无论是否为纯粹......这是推荐的编码实践。
...这也带来了存在意图(InOut)和意图(Out)存在的另一个原因,因为Pure必须拥有所有Arg的Intent声明,会有一些只有Out的Args,而其他的是InOut(也就是说,没有In,InOut和Out Intents的每一个都很难有。)
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 vars。呼叫s / r中的A()是"相同的A"就像在s / r中使用的那样(当然,在s / r中,可以制作A()或其他任何东西的副本,这会产生额外的工作/空间要求,但这是另一回事)。
正是由于这个原因,即使对于大阵列Arg等,Intent也不会直接影响性能。
B)关于"传递价值"混淆:尽管上面的各种反应确实证实使用Intent并没有通过值"但澄清此事可能会有所帮助。
将措辞改为" Intent始终以引用方式传递"可能会有所帮助。这与"不是通过值"不一样,这是一个重要的微妙之处。值得注意的是,不仅Intents" byRef",Intent可以预防值传递。
虽然有特殊/更复杂的设置(例如混合语言Fortran DLL等)需要进行大量额外讨论,但对于#34;标准Fortran"的大部分内容,Args都会通过通过参考。这个" Intent微妙的演示"可以看到" Glazer Guys"例如:as:
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声明为Value的Arg的任何尝试都将由于" collision"而失败。传球风格。
如果必须" Out"或" InOut"和"价值",然后一个人不能使用意图:这不仅仅是简单地说"它不是通过值"。