Fortran passing parameters with brackets prevents changes

拟墨画扇 提交于 2021-01-20 05:16:22

问题


In this question I asked about a method to explicitly prevent passed arguments to change. An obvious solutions is defining copys of the arguments and operate the algorithm on those copys. However in the comment I was pointed to the fact, that I could call the function and wrapp the argument I didn't want to change in brackets. This would have the same effect as creating a copy of that passed variables so that it would not change. But I don't understand how it works and what the brackets are actually doing. So could someone explain it to me?

Here is a simple example where the behaviour occurs as I described.

  1 program argTest                                                                 
  2   implicit none                                                                 
  3   real            ::  a, b, c                                                   
  4                                                                                 
  5   interface       !optional interface                                                              
  6     subroutine change(a,b,c)                                                    
  7       real           ::  a, b, c                                                
  8     end subroutine change                                                       
  9   end interface                                                                 
 10                                                                                 
 11   write(*,*) 'Input a,b,c: '                                                    
 12   read(*,*) a, b, c                                                             
 13                                                                                 
 14   write(*,*) 'Values at start:'                                                 
 15   write(*,*)'a:', a                                                             
 16   write(*,*)'b:', b                                                             
 17   write(*,*)'c:', c                                                             
 18                                                                                 
 19                                                                                 
 20   call change((a),b,c)                                                          
 21   write(*,*)'Values after calling change with brackets around a:'               
 22   write(*,*)'a:', a                                                             
 23   write(*,*)'b:', b                                                             
 24   write(*,*)'c:', c                                                             
 25                                                                                 
 26                                                                                 
 27   call change(a,b,c)                                                            
 28   write(*,*)'Values after calling change without brackets:'                     
 29   write(*,*)'a:', a                                                             
 30   write(*,*)'b:', b                                                             
 31   write(*,*)'c:', c                                                             
 32                                                                                 
 33 end program argTest                                                             
 34                                                                                 
 35                                                                                 
 36 subroutine change(a,b,c)                                                        
 37   real           ::  a, b, c                                                    
 38                                                                                 
 39   a = a*2                                                                       
 40   b = b*3                                                                       
 41   c = c*4                                                                       
 42                                                                                 
 43 end subroutine change                                                           
 44      
 45
 46                                                              

回答1:


I think the explanation is this, though I can't point to a part of the standard that makes it explicit, ...

(a) is an expression whose result is the same as a. What gets passed to the subroutine is the result of evaluating that expression. Fortran is disallowing an assignment to that result, just as it would if you passed cos(a) to the subroutine. I guess that the result of (a) is almost exactly the same as a copy of a, which might explain the behaviour that is puzzling OP.

I don't have Fortran on this computer, but if I did I'd try a few more cases where the difference between a and (a) might be important, such as

(a) = some_value

to see what the compiler makes of them.

@IanH's comment, below, points out the relevant part of the language standard.




回答2:


The syntax (a), in the context of the code in the question, is an expression. In the absence of pointer results, an expression is evaluated to yield a value. In this case the value of the expression is the same as the value of the variable a.

While the result of evaluating the expression (a), and the variable a, have the same value, they are not the same thing - the value of a variable is not the same concept as the variable itself. This is used in some situations where the same variable needs to be supplied as both an input argument and as a separate output argument, that would otherwise run afoul of Fortran's restrictions on aliasing of arguments.

HOWEVER - as stated above - in the absence of a pointer result, the result of evaluating an expression is a value, not a variable. You are not permitted to redefine a value. Conceptually, it makes it no sense to say "I am going to change the meaning of the value 2", or "I am going to change the meaning of the result of evaluating 1 + 1".

When you use such an expression as an actual argument, it must not be associated with a dummy argument that is redefined inside the procedure.

Inside the subroutine change, the dummy argument that is associated with the value of the expression (a) is redefined. This is non-conforming.

Whether a copy is made or not is an implementation detail that you cannot (and must not) count on - the comment in the linked question is inaccurate. For example, a compiler that is aware of this restriction discussed above knows the subroutine change cannot actually change the first argument in a conforming way, may know that a is not otherwise visible to change, and therefore decide that it doesn't need to make a temporary copy of a for the expression result.

If you need to make a temporary copy of something, then write the statements that make a copy.

real :: tmp_a
...
tmp_a = a
call change(tmp_a, b, c)



回答3:


It may be interesting to actually print the address of the actual and dummy arguments using (non-standard) loc() function and compare them, for example:

program main
    implicit none
    integer :: a

    a = 5
    print *, "address(a) = ", loc( a )

    call sub( 100 * a )
    call sub( 1 * a   )
    call sub( 1 * (a) )
    call sub( (a)     )
    call sub( a       )
contains

subroutine sub( n )
    integer :: n
    n = n + 1
    print "(2(a,i4,3x),a,i18)", "a=", a, " n=", n, "address(n) =", loc( n )
end subroutine

end program

The output become like this, which shows that a temporary variable containing the result of an expression is actually passed to sub() (except for the last case).

# gfortran-6
 address(a) =       140734780422480
a=   5    n= 501   address(n) = 140734780422468
a=   5    n=   6   address(n) = 140734780422464
a=   5    n=   6   address(n) = 140734780422460
a=   5    n=   6   address(n) = 140734780422456
a=   6    n=   6   address(n) = 140734780422480

# ifort-16
 address(a) =        140734590990224
a=   5    n= 501   address(n) = 140734590990208
a=   5    n=   6   address(n) = 140734590990212
a=   5    n=   6   address(n) = 140734590990216
a=   5    n=   6   address(n) = 140734590990220
a=   6    n=   6   address(n) = 140734590990224

# Oracle fortran 12.5
address(a) =  6296328
a=   5    n= 501   address(n) = 140737477281416
a=   5    n=   6   address(n) = 140737477281420
a=   5    n=   6   address(n) = 140737477281424
a=   5    n=   6   address(n) = 140737477281428
a=   6    n=   6   address(n) =         6296328

(It is interesting that Oracle uses a very small address for a for some reason... though other compilers use very similar addresses.)

[ Edit ] Acoording to the above answer by Ian, it is illegal to assign a value to the memory resulting from an expression (which is a value = constant, not a variable). So please take the above code just as an attempt to confirm that what is passed with (...) is different from the original a.



来源:https://stackoverflow.com/questions/40700499/fortran-passing-parameters-with-brackets-prevents-changes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!