In a subroutine or function an input variable can be defined with intent(in) and the compiler assures that within the subroutine the variable can not be altered. As soon as the variable is passed (by reference) to another subroutine this subroutine is able to alter the variable without compiler warning.
This was tested with gfortran with the code:
program Test
integer i
i = 21 ! half the truth
call test(i)
write (*,*) "21 expected, but is 42: ", i
end program
subroutine test(i)
integer, intent(in) :: i
call doSomethingNasty(i)
end subroutine
subroutine doSomethingNasty(i)
integer :: i
i = 42 ! set the full truth ;-)
end subroutine
My questions are:
test((i))
. For numeric variables, this is understandable and ok, but this seems to work with gfortran for arrays, derived types and pointers, too. Does this work with other compilers, too? Is it a safe way to protect my local variables?A function must return a single value, and can be invoked from within expressions, like a write statement, inside an if declaration if (function) then , etc. A subroutine does not return a value, but can return many values via its arguments and can only be used as a stand-alone command (using the keyword call ).
In the main program, a subroutine is activated by using a CALL statement which include the subroutine name followed by the list of inputs to and outputs from the subroutine surrounded by parenthesis.
Functions are similar to subroutines, except that they return a value. A function commonly carries out some calculations and reports the result to the caller. Subroutines perform a task but do not report anything to the calling program. A function cannot change the value of the actual arguments .
With sufficient compiler options gfortran generates a warning for your example, that an implicit interface is used.
If you make the interface explicit by placing the subroutines into a module, and use intents for all arguments, gfortran will catch the problem:
module mysubs
contains
subroutine test(i)
integer, intent(in) :: i
call doSomethingNasty(i)
end subroutine
subroutine doSomethingNasty(i)
integer, intent (inout) :: i
i = 42 ! set the full truth ;-)
end subroutine
end module mysubs
program Test_intent_in
use mysubs
integer i
i = 21 ! half the truth
call test(i)
write (*,*) "21 expected, but is 42: ", i
end program Test_intent_in
gfortran gives error message:
call doSomethingNasty(i)
1
Error: Procedure argument at (1) is INTENT(IN) while interface specifies INTENT(INOUT)
When pass the argument "(i)" you are passing an expression rather than a variable. The expression is not definable and thus should not be used as an actual argument for an "out" or "inout" dummy argument.
Another approach for argument "safety": you can also use the "value" attribute in the declaration of a dummy argument to essentially make a local copy of the argument and guarantee that the actual argument won't be altered.
Edit: As kemiisto pointed out, "contains" also makes the interface known. I don't like "contains" because the variable scoping ... all variables of the parent program are visible. Try this test code out:
PROGRAM contains_tst
INTEGER :: i, m
i = 21
m = 22
CALL test(m)
CONTAINS
SUBROUTINE test(j)
INTEGER, INTENT(IN) :: j
write (*, *) i, j
END SUBROUTINE test
END PROGRAM contains_tst
As soon as the variable is passed (by reference)
A warning note: Fortran standard does not specify how variables are passed (by reference, by value or in any other way). This is implementation dependent. Fortran is quite different from C/C++. Better stop to think in C-way. It will be misleading.
1) Yes and no. It is implementation dependent. First of all INTENT attribute specifies your intentions. As you can see in Fortran standard, Section 5.3.10, NOTE 5.17 (you can get the Final Draft of so-called Fortran 2008 by link at the beginning of this page http://fortranwiki.org/fortran/show/Fortran+2008):
Argument intent specifications serve several purposes in addition to documenting the intended use of dummy arguments. A processor can check whether an INTENT (IN) dummy argument is used in a way that could redefine it. [...]
compiler ("processor") can (not should) check such things.
Secondly (as I already mentioned) you can not be sure that for argument with INTENT(IN) compiler will choose to pass it by value and not by reference. In this case the choice was by reference. At least it seems that i in test subroutine was passed by reference. The next subroutine. The default INTENT is INOUT. That is why it is possible to change the value of argument i (with unspecified that's why default INTENT) in doSomethingNasty. Once again i was passed by reference. Or maybe it even was "copy-in/copy-out". Such freedom exists to allow compiler perform optimizations.
2) No. If I understand you correctly you need something similar to constant references (to achieve what is called "const correctness"). But we do not even have references in Fortran, so obviously there are no constant references.
3) There is a way to protect local variables. As M. S. B. pointed out in his answer place your subroutines in MODULEs (or in CONTAINS section of main program) and always specify INTENT attributes for variables. I've tried to compile the code below with different Fortran compilers available to me
PROGRAM main
INTEGER :: i
i = 21
CALL test(i)
WRITE (*,*) "21 expected, but is 42: ", i
CONTAINS
SUBROUTINE test(i)
INTEGER, INTENT(IN) :: i
CALL do_something_nasty(i)
END SUBROUTINE test
SUBROUTINE do_something_nasty(i)
INTEGER, INTENT(INOUT) :: i
i = 42
END SUBROUTINE do_something_nasty
END PROGRAM main
and all compilers (GNU, Intel, Open64, Portland and g95) issued an error message. I think that other compilers (Pathscale, IBM) will behave the same way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With