Meaning of the INTENT of arguments/variables within subroutines and functions in Fortran 90
I have a few questions about the INTENT
of variables within a subroutine in Fortran. For example, several weeks ago, I posted a question about a different Fortran topic (In Fortran 90, what is a good way to write an array to a text file, row-wise?), and one of the replies included code to define tick
and tock
commands. I have found these useful to time my code runs. I have pasted tick
and tock
below and used them in a simple example, to time a DO
loop:
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
This leads to my first question about INTENT
(my second question, below, is about subroutine or function arguments/variables whose INTENT is not explicitly specified):
To start the timer, I write
CALL tick(myclock)
, wheremyclock
is an integer. The header of the subroutine isSUBROUTINE tick(t)
, so it accepts the dummy integert
as an argument. However, inside the subroutine,t
is given INTENT(OUT):INTEGER, INTENT(OUT) :: t
. How can this be? My naive assumption is that INTENT(OUT) means that the value of this variable may be modified and will be exported out of the subroutine--and not read in. But clearlyt
is being read into the subroutine; I am passing the integermyclock
into the subroutine. So sincet
is declared as INTENT(OUT), how can it be thatt
seems to also be coming in?I notice that in the function
tock(t)
, the integer variablesnow
andclock_rate
are not explicitly given INTENTs. Then, what is the scope of these variables? Arenow
andclock_rate
only seen within the function? (Sort of like INTENT(NONE) or INTENT(LOCAL), although there is no such syntax?) And, while this is a function, does the same hold true for subroutines? Sometimes, when I am writing subroutines, I would like to declare "temporary" variables like this--variables that are only seen within the subroutine (to modify input in a step preceding the assignment of the final output, for example). Is this what the lack of a specified INTENT accomplishes?
I looked in a text (a Fortran 90 text by Hahn) and in it, he gives the following brief description of argument intent:
Argument intent. Dummy arguments may be specified with an intent attribute, i.e. whether you intend them to be used as input, or output, or both e.g.
SUBROUTINE PLUNK(X, Y, Z)
REAL, INTENT(IN) :: X
REAL, INTENT(OUT) :: Y
REAL, INTENT(INOUT) :: Z
...
If intent is IN, the dummy argument may not have its value changed inside the subprogram.
If the intent is OUT, the corresponding actual argument must be a variable. A call such as
CALL PLUNK(A, (B), C)
would generate a compiler error--(B) is an expression, not a variable.
If the intent is INOUT, the corresponding actual argument must again be a variable.
If the dummy argument has no intent, the actual argument may be a variable or an expression.
It is recommended that all dummy arguments be given an intent. In particular, all function arguments should have intent IN. Intent may also be specified in a separate statement, e.g. INTENT(INOUT) X, Y, Z.
The above text seems not even to mention argument/variable scope; it seems to be mainly talking about whether or not the argument/variable value may be changed inside the subroutine or function. Is this true, and if so, what can I assume about scope with respect to INTENT?
You're mostly right about the intent, but wrong about the semantics of tick(). The tick routine
SUBROUTINE tick(t)
INTEGER, INTENT(OUT) :: t
CALL system_clock(t)
END SUBROUTINE tick
does output a value; the intent, which is passed out, is the value of the system clock at the time the subroutine is called. Then tock() uses that value to calculate the time elapsed, by taking that time as an input, and comparing it to the current value of 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
As to scope: intent(in) and intent(out) necessarily only apply to "dummy arguments", variables that are passed in the argument list of a function or subroutine. For instance, in the above examples, the variables are locally referred to as t
, because that's what the corresponding dummy argument is called, but the variable necessarily has some existance outside this routine.
On the other hand, the variables now
and clock_rate
are local variables; they only exist in the scope of this routine. They can have no intent
clauses, because they can not take values passed in nor pass values out; they exist only in the scope of this routine.
Compilers are not required to detect all mistakes by the programmer. Most compilers will detect fewer mistakes by default and become more rigorous via compilation options. With particular options a compiler is more likely to detect a violation of argument intent and output a diagnostic message. This can be helpful in more quickly detecting a bug.
The difference between declaring no intent and intent(inout) is subtle. If the dummy is intent (inout), the actual argument must be definable. One case of a non-definable argument is a constant such as "1.0". It makes no sense to assign to a constant. This can be diagnosed at compile time. If the dummy argument has no specified intent, the actual argument must be definable if it is assigned to during execution of the procedure. This is much more difficult to diagnose since it might depend on program flow (e.g., IF statements). See Fortran intent(inout) versus omitting intent
After a quick search, I found this question: What is the explicit difference between the fortran intents (in,out,inout)?
From that I learned: "Intents are just hints for the compiler, and you can throw that information away and violate it." -- from The Glazer Guy
So my guess to your first question is: the intent(OUT) assignment only tells the compiler to check that you are actually passing a variable to the tick() subroutine. If you called it like so:
call tick(10)
you'd get a compilation error. The answers to the question linked above also discusses the differences between intents.
For your second question, I think it's important to distinguish between arguments and local variables. You can assign intents to the the arguments to your subroutine. If you don't assign an intent to your arguments, then the compiler can't help you make sure you are calling the subroutines correctly. If you don't assign intents and call the subroutine incorrectly (eg the way tick() was called above), you'll get an error at run time (Segmentation Fault) or some sort of erroneous behavior.
Your subroutines can also have local variables that act as temporary variables. These variables cannot have intents. So the now and clock_rate variables in your tock subroutine are local variables and should not have intents. Try to give them intents and see what happens when you compile. The fact that they don't have intents does not mean the same thing as an argument without an intent. These two variables are local and are only known to the subroutine. Arguments without intent can still be used to pass information to/from a subroutine; there must be default intent, similar to intent(inout), but I have no documentation to prove this. If I find that, I'll edit this answer.
EDIT: Also you might want to see this page for a discussion of issues resulting from INTENT(OUT) declarations. It's an advanced scenario, but I thought it might be worth documenting.
精彩评论