Fortran 90中子例程和函数中的参数/变量的INTENT的含义

时间:2011-08-08 16:11:58

标签: scope fortran fortran90

我对Fortran中子例程中的INTENT变量有几个问题。例如,几周前,我发布了一个关于不同Fortran主题(In Fortran 90, what is a good way to write an array to a text file, row-wise?)的问题,其中一个回复包括定义ticktock命令的代码。我发现这些对我的代码运行有用。我在下方粘贴了ticktock并在一个简单示例中使用它们来计算DO循环:

MODULE myintenttestsubs

  IMPLICIT NONE

CONTAINS

SUBROUTINE tick(t)
  INTEGER, INTENT(OUT) :: t
  CALL system_clock(t)
END SUBROUTINE tick

! returns time in seconds from now to time described by t
REAL FUNCTION tock(t)
  INTEGER, INTENT(IN) :: t
  INTEGER :: now, clock_rate

  CALL system_clock(now,clock_rate)

  tock = real(now - t)/real(clock_rate)
END FUNCTION tock

END MODULE myintenttestsubs

PROGRAM myintenttest
  USE myintenttestsubs
  IMPLICIT NONE
  INTEGER :: myclock, i, j
  REAL :: mytime

  CALL tick(myclock)

  ! Print alphabet 100 times
  DO i=1,100
     DO j=97,122
        WRITE(*,"(A)",ADVANCE="NO") ACHAR(j)
     END DO
  END DO

  mytime=tock(myclock)
  PRINT *, "Finished in ", mytime, " sec"
END PROGRAM myintenttest

这引出了我关于INTENT的第一个问题(我的第二个问题,关于子程序或函数参数/变量,其INTENT 明确指定):

  1. 要启动计时器,我写CALL tick(myclock),其中myclock是整数。子例程的标头是SUBROUTINE tick(t),因此它接受伪整数t作为参数。但是,在子例程中,t被赋予INTENT(OUT):INTEGER, INTENT(OUT) :: t。怎么会这样?我天真的假设是,INTENT(OUT)意味着可以修改此变量的值并将其从子例程中导出 - 并且读入。但显然t正在读入子程序;我将整数myclock传递给子例程。所以,由于t被声明为INTENT(OUT),t似乎也

  2. 我注意到在函数tock(t)中,整数变量nowclock_rate没有明确给出INTENT。那么,这些变量的范围是什么?在函数中只看到nowclock_rate 吗? (类似于INTENT(NONE)或INTENT(LOCAL),虽然没有这样的语法?)而且,虽然这是一个函数,但子例程是否也适用?有时,当我编写子程序时,我想声明像这样的“临时”变量 - 仅在子程序中看到的变量(例如,在最终输出的赋值之前的步骤中修改输入)。这是缺少指定的INTENT吗?

  3. 我查看了一个文本(Fortran 90 text by Hahn)并在其中,他给出了以下对参数意图的简要描述:

      

    参数意图。虚拟参数可以用。指定    intent 属性,即您是否打算将它们用作输入,   或输出,或两者,例如

    SUBROUTINE PLUNK(X, Y, Z)
    REAL, INTENT(IN) :: X
    REAL, INTENT(OUT) :: Y
    REAL, INTENT(INOUT) :: Z
    ...
    
      

    如果intent为IN,则伪参数的值可能不会更改   在子程序内。

         

    如果意图是OUT,则相应的实际参数必须为a   变量。一个叫

    的电话      

    CALL PLUNK(A,(B),C)

         

    会产生编译错误 - (B)是表达式,而不是变量。

         

    如果意图是INOUT,则必须再次使用相应的实际参数   是一个变量。

         

    如果伪参数具有 no intent,则实际参数可以是a   变量或表达式。

         

    建议给所有伪参数赋予意图。在   特别是,所有函数参数都应具有意图IN。意图可能   也可以在单独的声明中指定,例如INTENT(INOUT)X,Y,Z。

    上面的文字似乎甚至没有提到参数/变量范围;似乎主要是讨论参数/变量 value 是否可以在子例程或函数内部进行更改。这是真的,如果是这样,我可以假设关于INTENT的范围?

3 个答案:

答案 0 :(得分:3)

你对这个意图大多是正确的,但对于tick()的语义是错误的。刻度例程

SUBROUTINE tick(t)
  INTEGER, INTENT(OUT) :: t
  CALL system_clock(t)
END SUBROUTINE tick

输出一个值;传递的意图是调用子程序时系统时钟的值。然后tock()使用该值计算经过的时间,将该时间作为输入,并将其与system_clock的当前值进行比较:

REAL FUNCTION tock(t)
  INTEGER, INTENT(IN) :: t
  INTEGER :: now, clock_rate

  CALL system_clock(now,clock_rate)

  tock = real(now - t)/real(clock_rate)
END FUNCTION tock

关于范围:intent(in)和intent(out)必然仅适用于“伪参数”,即在函数或子例程的参数列表中传递的变量。例如,在上面的例子中,变量在本地被称为t,因为这就是调用相应的伪参数的内容,但变量必然存在于此例程之外。

另一方面,变量nowclock_rate是局部变量;它们只存在于此例程的范围内。它们没有intent个子句,因为它们不能传入传入的值,也不能传递值;它们只存在于这个例程的范围内。

答案 1 :(得分:2)

编译器不需要检测程序员的所有错误。大多数编译器默认会检测到更少的错误,并通过编译选项变得更加严格。对于特定选项,编译器更有可能检测到违反参数意图并输出诊断消息。这有助于更快地检测到错误。

宣布无意图和意图(inout)之间的区别是微妙的。如果虚拟是intent(inout),则实际参数必须是可定义的。一个不可定义的论证是一个常数,如“1.0”。分配常数是没有意义的。这可以在编译时诊断出来。如果伪参数没有指定的意图,则实际参数必须是可定义的,如果在执行过程期间将其赋值。这要诊断起来要困难得多,因为它可能取决于程序流程(例如,IF语句)。见Fortran intent(inout) versus omitting intent

答案 2 :(得分:1)

快速搜索后,我发现了这个问题: What is the explicit difference between the fortran intents (in,out,inout)?

由此我了解到:“意图只是编译器的提示,你可以抛弃那些信息而违反它。” - 来自The Glazer Guy

所以我对第一个问题的猜测是:intent(OUT)赋值只告诉编译器检查你实际上是否正在将一个变量传递给tick()子程序。如果你这样称呼它:

call tick(10)

你会收到编译错误。上面链接的问题的答案也讨论了意图之间的差异。

对于你的第二个问题,我认为区分参数和局部变量很重要。您可以将意图分配给子例程的参数。如果没有为参数赋予意图,那么编译器无法帮助您确保正确调用子例程。如果你没有分配意图并错误地调用子程序(例如上面调用tick()的方式),你将在运行时(分段错误)或某种错误行为得到错误。

您的子例程还可以具有充当临时变量的局部变量。这些变量不能有意图。因此,tock子例程中的 now clock_rate 变量是局部变量,不应该有意图。 尝试给出它们意图并看看编译时会发生什么。它们没有意图这一事实并不意味着没有意图的参数。这两个变量是局部的,只有子程序知道。没有意图的参数仍可用于向/从子例程传递信息;必须有默认意图,类似于意图(inout),但我没有文件来证明这一点。如果我找到了,我会编辑这个答案。

修改 另外,您可能希望查看this页面,以了解由INTENT(OUT)声明产生的问题。这是一个高级方案,但我认为值得记录。