在被调用的子例程/函数中,强制Fortran中的intent(in)声明变量也是常量 [英] Enforce intent(in) declared variables in Fortran as constant also in called subroutines/functions

查看:396
本文介绍了在被调用的子例程/函数中,强制Fortran中的intent(in)声明变量也是常量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个子程序或函数中,输入变量可以用intent(in)定义,并且编译器确保在子程序中该变量不能被改变。只要变量被传递(通过引用)到另一个子例程,该子例程就能够在没有编译器警告的情况下更改变量。



这是用gfortran测试过的代码:

  program Test 
整数i
i = 21!一半是真的
call test(i)
write(*,*)21 expected,but is 42:,i
end program

subroutine test( i)
整数,意图(in):: i
调用doSomethingNasty(i)
结束子程序

子程序doSomethingNasty(i)
integer ::我
i = 42!设置完整的真相;-)
结束子程序

我的问题是:


  1. 这是所有编译器的正常行为吗?
  2. 是否有办法强制编译器确保变量是否真的是不变的,这种变化会以编译器错误的形式出现?我的意思是类似于C / C ++中的const关键字,它也针对被调用的函数进行了检查,该函数也需要确保该常量被相应地处理,并且没有引用正在转义。

  3. 我发现通过传递它通过像code> test((i))这样的表达式将变量传递给子例程的可能性。对于数值变量来说,这是可以理解的,并且可以,但是这似乎也适用于数组,衍生类型和指针的gfortran。这是否也适用于其他编译器?这是保护我的本地变量的安全方法吗?
  4. 使用足够的编译器选项,gfortran生成一个隐含的接口将被使用。



    如果通过将子例程放入模块并使用所有参数的意图使接口显式化,gfortran会发现问题:

     模块mysubs 

    包含

    子例程测试(i)
    整数,意图(in):: i
    调用doSomethingNasty(i)
    结束子程序

    子程序doSomethingNasty(i)
    整数, intent(inout):: i
    i = 42!设置完整的真相;-)
    结束子程序


    结束模块mysubs


    程序Test_intent_in

    使用mysubs

    整数i
    i = 21!一半的真相
    测试(i)
    写(*,*)21预期,但是是42:,

    结束程序Test_intent_in

    gfortran给出错误信息:

     调用doSomethingNasty(i)
    1
    错误:(1)处的过程参数是INTENT(IN),而接口指定了INTENT(INOUT)

    当传递参数(i)时,您传递的是表达式而不是变量。该表达式不可定义,因此不应该用作out或inout虚拟参数的实际参数。

    参数safety的另一种方法是:您也可以在伪参数声明中使用value属性来本质上构建参数的本地副本并保证不会改变实际参数。



    编辑:
    kemiisto指出,contains也使界面变得可知。我不喜欢包含,因为变量范围...父程序的所有变量都是可见的。试试这个测试代码:

      PROGRAM contains_tst 

    INTEGER :: i,m

    i = 21
    m = 22
    CALL测试(m)

    包含

    子程序测试(j)
    INTEGER,INTENT (IN):: j
    write(*,*)i,j
    END SUBROUTINE test

    END PROGRAM contains_tst


    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:

    1. Is this the normal behaviour for all compilers?
    2. Is there a way to force the compilers to assure that the variable is really constant and that alterations would be presented as compiler errors? I mean something like the const keyword in C/C++ which is also checked against the called functions which also need to assure that the constant is treated accordingly and that no reference is escaping.
    3. I found the possibility to pass the variable to the subroutine by "value" via passing it trough an expression like 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?

    解决方案

    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
    

    这篇关于在被调用的子例程/函数中,强制Fortran中的intent(in)声明变量也是常量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆