在被调用的子例程/函数中,将Fortran中的intent(in)声明变量强制为常量

时间:2011-08-28 19:22:02

标签: fortran

在子程序或函数中,可以使用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

我的问题是:

  1. 这是所有编译器的正常行为吗?
  2. 有没有办法强制编译器确保变量真的是常量,并且更改会显示为编译器错误?我的意思是类似于C / C ++中的const关键字,它也会根据被调用函数进行检查,这也需要确保相应地处理常量并且没有引用转义。
  3. 我发现可以通过“{”将变量传递给子程序,方法是通过类似test((i))的表达式传递它。对于数值变量,这是可以理解的,但是这似乎也适用于gfortran的数组,派生类型和指针。这也适用于其他编译器吗?这是保护我的本地变量的安全方法吗?

2 个答案:

答案 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)的行为方式也一样。