我对Fortran中子例程中的INTENT
变量有几个问题。例如,几周前,我发布了一个关于不同Fortran主题(In Fortran 90, what is a good way to write an array to a text file, row-wise?)的问题,其中一个回复包括定义tick
和tock
命令的代码。我发现这些对我的代码运行有用。我在下方粘贴了tick
和tock
并在一个简单示例中使用它们来计算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 未明确指定):
要启动计时器,我写CALL tick(myclock)
,其中myclock
是整数。子例程的标头是SUBROUTINE tick(t)
,因此它接受伪整数t
作为参数。但是,在子例程中,t
被赋予INTENT(OUT):INTEGER, INTENT(OUT) :: t
。怎么会这样?我天真的假设是,INTENT(OUT)意味着可以修改此变量的值并将其从子例程中导出 - 并且不读入。但显然t
正在读入子程序;我将整数myclock
传递给子例程。所以,由于t
被声明为INTENT(OUT),t
似乎也
我注意到在函数tock(t)
中,整数变量now
和clock_rate
没有明确给出INTENT。那么,这些变量的范围是什么?在函数中只看到now
和clock_rate
吗? (类似于INTENT(NONE)或INTENT(LOCAL),虽然没有这样的语法?)而且,虽然这是一个函数,但子例程是否也适用?有时,当我编写子程序时,我想声明像这样的“临时”变量 - 仅在子程序中看到的变量(例如,在最终输出的赋值之前的步骤中修改输入)。这是缺少指定的INTENT吗?
我查看了一个文本(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的范围?
答案 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
,因为这就是调用相应的伪参数的内容,但变量必然存在于此例程之外。
另一方面,变量now
和clock_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)声明产生的问题。这是一个高级方案,但我认为值得记录。