Fortran intent(inout) versus omitting intent
Good practice dictates that subroutine 开发者_运维知识库arguments in Fortran should each have a specified intent (i.e. intent(in)
, intent(out)
or intent(inout)
as described this question):
subroutine bar (a, b)
real, intent(in) :: a
real, intent(inout) :: b
b = b + a
...
However, not specifying an intent is valid Fortran:
subroutine bar (a, b)
real, intent(in) :: a
real :: b
b = b + a
...
Are there any real differences beyond compile time checking for an argument specified as intent(inout)
and an argument without a specified intent? Is there anything I should worry about if I'm retrofitting intents to older, intent free, code?
According to The Fortran 2003 Handbook by Adams, et al., there is one difference between an intent(inout) argument and argument without specified intent. The actual argument (i.e., in the caller) in the intent(inout) case must always be definable. If the intent is not specified, the argument must be definable if execution of the subroutine attempts to define the dummy argument. definable means setting the value: dummy_arg = 2.0. Clearly the actual argument should be a variable if this is done. For intent(inout) the actual argument must be definable whether or not the subroutine does this. Without no intent specified, it depends on what happens on that particular invocation of the subroutine -- if the subroutine doesn't define the variable, it is OK; if it does, than there is a problem -- cases such as writing to an actual argument that is a constant will obviously cause problems.
This doesn't mean that the compiler will diagnose all of these cases -- what the standard requires a compiler to diagnose is a different issue. It would be close to impossible to detect all errors of the intent-not-specified case requirement at compile time, since violations depend on the run-time flow of the code. It is much easier for the compiler to diagnose the intent(inout) case and warn you of problems with the code.
Your question prompts me to wonder (lots to do right now) whether you might encounter a difference in behaviour if your code passes a PARAMETER as an actual argument which your sub-program then attempts to write to. Without an INTENT declaration the compiler might let this go, leading to odd behaviour. With the declaration I'd expect a compile-time error.
You and I might think that there is no difference between INOUT and no INTENT declaration, but don't forget that there are a lot of old Fortran programs out there and that compatibility with older language versions is an important feature of new standards. If it was correct (but dodgy) FORTRAN77 then a lot of people expect their code to remain correct (still dodgy) with a Fortran 90+ compiler.
A quick read of the 2003 standard does indicate that there is a difference between INOUT and no INTENT, but more close reading is required. If you do test this let us know your conclusions; if I have time later I will test it myself and let you know.
To understand the role of intent in/out, you need to know that internally, Fortran effectively passes variables by reference. This isn't always the same as actually passing by reference.
If you pass an internal subsection of an 2-D array to a subroutine, ie: data(i1:i2, j1:j2)
, then Fortran copies that data into a contiguous section of memory, and passes the new address to the routine. Upon returning, the data is copied back into its original location.
By specifying INTENT
, the compiler can know to skip one of the copying operations.
It not only acts as a failsafe for modifying data that you want to remain unchanged, but also can speed up your code when dealing with large datasets.
To somehow expand M.S.B.'s answer, omitting intent
gives you more flexibility, at the cost of less compile-time checking. Without intent
, you can pass a literal constant if you know the code branch will not attempt to modify it, with intent(inout)
the compiler will probably bark at you if you try it. This may be useful with "dual" procedures that will modify or not some argument depending on the options, for example:
subroutine copy(x,a,readwrite)
integer :: x, a
integer, intent(in) :: readwrite
if (readwrite == 0) then
x = a
else
a = x
end if
end subroutine
If you want to add intent
to x
and a
, it must be inout
for both, because both are potentially read and modified. But that will not allow you to write e.g.
call copy(x,3,0) ! equiv. to x=3
call copy(42,a,1) ! equiv. to a=42
This example is quite silly, but think of a more elaborate subroutine that can read from or write to a file with some complex formatting. (I'm not saying that's the best solution, but it's something you can easily find.)
精彩评论