在子程序或函数中,可以使用intent(in)定义输入变量,并且编译器确保在子例程中不能更改变量。一旦变量(通过引用)传递给另一个子例程,该子例程就能够在没有编译器警告的情况下更改变量。
使用gfortran测试了这个代码:
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
我的问题是:
test((i))
的表达式传递它。对于数值变量,这是可以理解的,但是这似乎也适用于gfortran的数组,派生类型和指针。这也适用于其他编译器吗?这是保护我的本地变量的安全方法吗?答案 0 :(得分:7)
如果有足够的编译器选项,gfortran会为您的示例生成警告,即使用隐式接口。
如果通过将子例程放入模块来使接口显式化,并对所有参数使用intent,gfortran将会发现问题:
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给出了错误消息:
call doSomethingNasty(i)
1
Error: Procedure argument at (1) is INTENT(IN) while interface specifies INTENT(INOUT)
当传递参数“(i)”时,您传递的是表达式而不是变量。表达式不可定义,因此不应用作“out”或“inout”伪参数的实际参数。
参数“安全”的另一种方法:您还可以在伪参数的声明中使用“value”属性来基本上创建参数的本地副本,并保证实际参数不会被更改。
编辑: 正如kemiisto指出的那样,“包含”也使界面为人所知。我不喜欢“包含”,因为变量作用域...父程序的所有变量都是可见的。试试这个测试代码:
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
答案 1 :(得分:4)
一旦传递变量(通过引用)
警告说明:Fortran标准未指定变量的传递方式(通过引用,按值或以任何其他方式)。这取决于实现。 Fortran与C / C ++完全不同。最好停止用C-way思考。这会产生误导。
1)是和否。它取决于实现。首先,INTENT属性指定您的意图。正如您在Fortran标准中所见,第5.3.10节,注5.17(您可以通过本页开头的链接http://fortranwiki.org/fortran/show/Fortran+2008获得所谓Fortran 2008的最终草案):
参数意图规范除了具有多种用途外,还有其他用途 记录虚拟参数的预期用途。处理器可以检查 是否以可能的方式使用INTENT(IN)伪参数 重新定义它。 [...]
编译器(“处理器”)可以(不应该)检查这些事情。
其次(正如我已经提到的)你不能确定对于INTENT(IN)编译器的参数将选择通过值而不是通过引用传递它。在这种情况下,选择是参考。至少看来我在测试子程序中通过引用传递。下一个子程序。默认的INTENT是INOUT。这就是为什么可以在doSomethingNasty中更改参数i的值(未指定为默认INTENT的原因)。我再次通过参考传递。或者甚至可能是“复制/复制”。存在这种自由以允许编译器执行优化。
2)不。如果我理解正确你需要类似于常量引用的东西(实现所谓的“const正确性”)。但我们甚至没有在Fortran中引用,所以很明显没有常量引用。
3)有一种方法可以保护局部变量。正如M. S. B. pointed out在他的回答中将子程序放在MODULEs中(或主程序的CONTAINS部分)并始终为变量指定INTENT属性。我尝试使用不同的Fortran编译器编译下面的代码
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
所有编译器(GNU,Intel,Open64,Portland和g95)都发出错误消息。我认为其他编译器(Pathscale,IBM)的行为方式也一样。