7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Fortranで複数回呼ばれるルーチンの変数の初期化には気を付けよう![ SAVE属性 ]

Last updated at Posted at 2022-01-18

結論

  • 複数回呼ばれるsubroutineやfunctionではSAVE属性ではない変数は宣言時の初期化をしてはいけない

どうして変数の宣言時に初期化をしてはいけないか

  • 変数の宣言時に初期化をすると、その変数は自動的にSAVE属性になるため(お節介機能……)
  • このStackOverflowの回答 を参照のこと

SAVE属性

  • SAVE属性を付けた変数に起こることはibm XL FortranのSAVE属性の記事に書いてある
  • ibmの記事は少々難しい用語を用いて説明されているが、誤解を恐れず要約すると
    SAVE属性がついた変数は、一度変数を宣言したルーチンから抜けても
    次に同じルーチンが走ったとき前回の値が保持された状態になる
    という特徴がある
  • しかも変数の宣言時の初期化処理は最初の1回しか行われない

なのでSAVE属性にするつもりが無い変数で宣言時代入をすると
2回目以降の実行の結果が意図しないものになる!!!

検証

  • gfortran 9.3.0
  • ファイル : main.f90 , sub.f90
  • mainから10回subを呼んでsubで定義した数値 save_integer をインクリメントするプログラム

プログラム

main.f90
program main
    implicit none
    integer :: i

    do i = 1, 10
        call sub(i)
    end do
end program main
sub.f90
subroutine sub(i)
    implicit none
    integer,intent(in) :: i
    integer :: save_integer = 0 ! この初期化処理は最初の1回しか実行されない!!

    save_integer = save_integer + 1
    print *, save_integer," main: ", i
end subroutine sub

結果

shell
gfortran main.f90 sub.f90 -o a.out && ./a.out

           1  main:            1
           2  main:            2
           3  main:            3
           4  main:            4
           5  main:            5
           6  main:            6
           7  main:            7
           8  main:            8
           9  main:            9
          10  main:           10

上の結果からsave_integerにはSAVE属性を付けていないのに
integer :: save_integer = 0 が最初の1回しか実行されていないことがわかる

解決策

  • 変数の宣言とは別に初期値の代入を行う
sub.f90
subroutine sub(i)
    implicit none
    integer,intent(in) :: i
    ! integer :: save_integer = 0 ! 宣言時に0を代入するのではなく……
    integer :: save_integer ! 宣言時は変数の宣言だけを行って
    save_integer = 0 ! 代入は別に行う!!!


    save_integer = save_integer + 1
    print *, save_integer," main: ", i
end subroutine sub

このように書くと……

結果

shell
gfortran main.f90 sub.f90 -o a.out && ./a.out

           1  main:            1
           1  main:            2
           1  main:            3
           1  main:            4
           1  main:            5
           1  main:            6
           1  main:            7
           1  main:            8
           1  main:            9
           1  main:           10

subroutineが呼ばれるごとにsave_integer = 0 の処理が走るようになった!

思ったこと

  • Fortran以外の言語を触っている人ほど、宣言時代入をナチュラルに行ってバグらせそう
  • SAVE属性使いたいなら明示的にinteger,save :: i = 0 とするんだから宣言時代入でなぜ自動的にSAVE属性にするのか?コレガワカラナイ
7
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?