fortran66のブログ

fortran について書きます。

FORTRAN77での実引数の扱い

FORTRAN77 での再帰を使ったプログラムで気になるは、引数が参照渡しになっていることです。局所変数は、みな AUTOMATIC 変数として呼び出しごとに確保されるからよいわけですが、引数が参照渡しである場合、呼び出しの深さに関わらず実引数の内容が書き換えられることになってしまいます。したがって、引数は INTENT(IN) の属性を持っていないと不都合がでることになります。

FORTRAN77 で再帰を使った例

階乗

      PROGRAM TEST
      DO 10 I = 1, 12
        PRINT *, I, IFACT(I, IFACT)
10    CONTINUE
      STOP
      END

      FUNCTION IFACT(N, MYSELF)
      EXTERNAL MYSELF
      IF (N .LE. 1) THEN
        IFACT = 1
      ELSE
        IFACT = N * MYSELF(N - 1, MYSELF)
      END IF
      RETURN
      END

ところで、FORTRAN77 以降では、実引数に数式を書くことができるわけですが、その場合に仮引数が INTENT(IN OUT) 属性で返り値があるとすると、返り値を戻す変数が存在しないことになって困難が生じます。ゆえに、数式を渡していい仮引数は INTENT(IN) 属性を持たなければならないとされています。同様なことは関数を実引数とする時も問題になります。

ところが FORTRAN77 では、INTENT 指定ができず、リンク時のチェックも弱いので、この手の問題が思いがけず顔を出します。

有名な FORTRAN 昔話に、数値べたうちの副プログラム呼び出しの常数実引数が、副プログラム内部書き換えられてしまって、それが主プログラム中の別の場所の同じ数字のべたうちと共有されていて、以降の主プログラムの結果がみなおかしくなってしまった事件があります。

以下に規約を破って、計算式を INTENT(IN OUT) 属性を持つ引数に入れて、サブルーチン内で書き換えを行った結果を示します。サブルーチンでは、引数から1引かれます。

変数 i を実引数としてそのまま入れた場合には、i は1引かれて戻ってきます。ところが、実引数として i*1 の数式を与えると、あたかも call by value 型の引数のように、変数 i の値は保護されています。これは多分 i*1 の結果がおかれた番地が書き変わっているものと思われます。

このように色々予想外の結果が生じるので、INTENT 属性を明示しない副プログラムでは注意が必要になります。

実行結果とソースプログラム

      program test
      i = 1
      print *, 'main: before dec', i
      call dec(i * 1)
      print *, 'main: after  dec', i
      
      print *
      
      print *, 'main: before dec', i
      call dec(i)
      print *, 'main: after  dec', i
      stop
      end
      
      subroutine dec(n)
      print *, 'dec : before n-1', n
      n = n - 1
      print *, 'dec : after  n-1', n
      end