关于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
是一个整数 . 子程序的 Headers 是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),虽然没有这样的语法?)而且,虽然这是一个函数,但子例程是否也适用?有时,当我编写子程序时,我想声明"temporary"这样的变量 - 仅在子程序中看到的变量(例如,在最终输出的赋值之前的步骤中修改输入) . 这是缺少指定的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,则相应的实际参数必须是变量 . 诸如CALL PLUNK(A,(B),C)之类的调用会产生编译器错误 - (B)是表达式,而不是变量 . 如果意图是INOUT,则相应的实际参数必须再次是变量 . 如果伪参数没有意图,则实际参数可以是变量或表达式 . 建议给出所有伪参数的意图 . 特别是,所有函数参数都应具有意图IN . 意图也可以在单独的声明中指定,例如意图(INOUT)X,Y,Z .
上面的文字似乎甚至没有提到论证/变量范围;似乎主要是讨论在子例程或函数内是否可以更改参数/变量值 . 这是真的,如果是这样,我对于INTENT的范围有什么假设?
3 回答
你对这个意图大多是正确的,但对于tick()的语义是错误的 . 蜱常规
输出一个值;传递的意图是调用子程序时系统时钟的值 . 然后tock()使用该值计算经过的时间,将该时间作为输入,并将其与system_clock的当前值进行比较:
对于范围:intent(in)和intent(out)必然仅适用于"dummy arguments",在函数或子例程的参数列表中传递的变量 . 例如,在上面的例子中,变量在本地被称为
t
,因为这就是调用相应的伪参数,但变量必然在此例程之外存在 .另一方面,变量
now
和clock_rate
是局部变量;它们只存在于此例程的范围内 . 它们没有intent
子句,因为它们不能传入传入的值,也不能传递值;它们仅存在于此例程的范围内 .编译器不需要检测程序员的所有错误 . 大多数编译器默认会检测到更少的错误并变得更加严格通过编译选项 . 对于特定选项,编译器更有可能检测到违反参数意图并输出诊断消息 . 这有助于更快地检测错误 .
声明无意图和意图(inout)之间的区别是微妙的 . 如果虚拟是intent(inout),则实际参数必须是可定义的 . 一个不可定义的参数是一个常数,如"1.0" . 分配常数是没有意义的 . 这可以在编译时诊断出来 . 如果伪参数没有指定的意图,则实际参数必须是可定义的,如果在执行过程期间将其赋值 . 这要诊断起来要困难得多,因为它可能取决于程序流程(例如,IF语句) . 见Fortran intent(inout) versus omitting intent
快速搜索后,我发现了这个问题:What is the explicit difference between the fortran intents (in,out,inout)?
从那我学到:"Intents are just hints for the compiler, and you can throw that information away and violate it." - 来自The Glazer Guy
所以我对第一个问题的猜测是:intent(OUT)赋值只告诉编译器检查你实际上是否正在将一个变量传递给tick()子例程 . 如果你这样称呼它:
你会收到编译错误 . 上面链接的问题的答案也讨论了意图之间的差异 .
对于你的第二个问题,我认为区分参数和局部变量很重要 . 您可以将意图分配给子例程的参数 . 如果没有为参数赋予意图,那么编译器无法帮助您确保正确调用子例程 . 如果你没有分配意图并错误地调用子程序(例如上面调用tick()的方式),你将在运行时(分段错误)或某种错误行为中得到错误 .
您的子例程也可以具有充当临时变量的局部变量 . 这些变量不能有意图 . 因此,tock子例程中的 now 和 clock_rate 变量是局部变量,不应该有意图 . 尝试给它们意图并看看编译时会发生什么 . 事实上,他们不会编辑这个答案 .
EDIT: 另外,您可能希望查看this页面,以了解由INTENT(OUT)声明产生的问题 . 这是一个高级方案,但我认为值得记录 .