1

我有几个关于INTENTFortran 子程序中的变量的问题。例如,几周前,我发布了一个关于不同 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没有明确给出意图。那么,这些变量的作用域是什么?是否now只在函数clock_rate 可见?(有点像 INTENT(NONE) 或 INTENT(LOCAL),虽然没有这样的语法?)而且,虽然这是一个函数,但对子例程是否同样适用?有时,当我编写子例程时,我想声明这样的“临时”变量——这些变量只能在子例程中看到(例如,在分配最终输出之前的步骤中修改输入)。这就是缺乏特定意图的结果吗?

我查看了一个文本(Hahn 的 Fortran 90 文本),在其中,他对参数意图进行了以下简要描述:

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

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

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

如果意图是 OUT,则对应的实际参数必须是变量。一个电话如

CALL PLUNK(A, (B), C)

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

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

如果虚拟参数没有意图,则实际参数可能是变量或表达式。

建议为所有虚拟参数指定一个意图。特别是,所有函数参数都应该有意图 IN。也可以在单独的语句中指定意图,例如 INTENT(INOUT) X、Y、Z。

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

4

3 回答 3

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子句,因为它们不能接受传入的值,也不能传出值;它们只存在于这个例程的范围内。

于 2011-08-08T21:38:03.693 回答
2

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

声明没有意图和意图(inout)之间的区别是微妙的。如果虚拟对象是意图(inout),则实际参数必须是可定义的。不可定义参数的一种情况是常数,例如“1.0”。分配给常数是没有意义的。这可以在编译时诊断。如果虚拟参数没有指定的意图,则如果在过程执行期间分配了实际参数,则它必须是可定义的。这更难诊断,因为它可能取决于程序流(例如,IF 语句)。请参阅Fortran 意图(inout)与省略意图

于 2011-08-08T21:37:08.140 回答
1

经过快速搜索,我发现了这个问题: fortran 意图(in、out、inout)之间的显式区别是什么?

从中我了解到:“意图只是编译器的提示,您可以丢弃这些信息并违反它。” ——来自Glazer Guy

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

call tick(10)

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

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

您的子例程也可以具有充当临时变量的局部变量。这些变量不能有意图。所以 tock 子例程中的nowclock_rate变量是局部变量,不应该有意图。试着给他们一些意图,看看编译时会发生什么。他们没有意图的事实并不意味着与没有意图的争论相同。这两个变量是局部变量,只有子程序知道。没有意图的参数仍然可以用于向子程序传递信息或从子程序传递信息;必须有默认意图,类似于意图(inout),但我没有文件可以证明这一点。如果我发现,我会编辑这个答案。

编辑: 此外,您可能希望查看页面以讨论由 INTENT(OUT) 声明产生的问题。这是一个高级场景,但我认为它可能值得记录。

于 2011-08-08T20:35:51.930 回答