fortran意图(in,out,inout)之间的明显区别是什么?

时间:2009-06-18 09:01:44

标签: arguments fortran

在书籍中搜索了一段时间后,在stackoverflow和一般网络上,我发现很难找到对fortran参数意图之间真正差异的直接解释。我理解它的方式是:

  • intent(in) - 将实际参数复制到条目处的伪参数。
  • intent(out) - 伪参数指向实际参数(它们都指向内存中的相同位置)。
  • intent(inout) - 伪参数在本地创建,然后在过程完成时复制到实际参数。

如果我的理解是正确的,那么我也想知道为什么人们想要使用intent(out),因为intent(inout)需要更少的工作(不复制数据)。

3 个答案:

答案 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"和"价值",然后一个人不能使用意图:这不仅仅是简单地说"它不是通过值"。