• 締切済み

フォートランのサブルーチンの内部変数

すみません。科学技術系の人間でして、フォートランが現役なのです。 あるプログラムのことですが、サブルーチンの内部の変数についてサブルーチンを呼び出すごとに加算していくようなことを行っています。 do i=1,100 call abc(i)   ← サブルーチンabcを100回呼び出す enddo end subroutine abc(i) n=n+1        ←呼び出されるたびに和をとる。 write(*,*) n return end このようなシンプルなプログラムは問題ないのですが、少し複雑になるとどういうわけか、呼び出される度にnが初期化され、writeさせた結果常に1が表示されるという現象に会いました。 昔のコンパックフォートラン(MS-Fortran, DEC Fortranを継承)と、最近のIntel Fortran(最新版)で動作が異なります。前者では和を取ってくれますが、後者では初期化されて常に1が出力されます。(上記のプログラムは問題なしです。もう少し長いプログラムでの問題です。) 言語仕様の問題(すなわちフォートランのバージョン)なのか、コンパイラ仕様の問題なのか、C言語のように宣言の仕方で変ったりするようになったというようなことはないでしょうか。 以下に示すように和をとる変数をメインプログラム側に一旦見えるようにすると、インテル版でも思ったとおりの動作をします。 call abc(i,n) suboroutine abc(i,n) としてnをメイン側に露出する。 よろしくお願いします。

みんなの回答

  • ultraCS
  • ベストアンサー率44% (3956/8947)
回答No.3

元々のFORTRANの仕様で、サブルーチン内の変数は保証されなくてもかまいません。これは、FORTRANの最初の仕様が決められた当時のコンピュータではメモリ容量が少なかったため、サブルーチンは呼び出される度にディスクからメモリにロードされていたため、変更された内容を保存する事ができなかったためです。これは、サブルーチンの変数をスタックに置かないGCOSのFORTRANなどでも同様です。 確実に値を保持したいなら、以下のいずれかの方法を使ってください。 ・Nを引数に含める ・Nを名前無し共通領域(ブランクコモン)に置く ・Nを名前付き共通領域(レーベルドコモン)に置く ・SAVE文を使う

  • Tasuke22
  • ベストアンサー率33% (1799/5383)
回答No.2

初期化されていない、また保存もされていない変数は、 コンパイラが変われば扱いは変わりますし、コンパイル オプションが変わっただけでも値が変わる性質のものです。 変数の扱い方に誤りがあると言わざるを得ません。 一般的に、サブルーチンが自分を呼び出した回数を計算する こと自体、機能的強度の落ちる性質を持つように思われます。 複数の場所から呼び出すので仕方がない場合でも、なにか工 夫をしたいところです。 今回のケースに限って言えば、nの計算をする必要はなく、 目的の値はiにあります。

  • f272
  • ベストアンサー率46% (7998/17100)
回答No.1

局所変数は,実行時にスタックに積まれるのが普通ですから,変数の値は保持されなくても文句は言えません。サブルーチン内で値を保持したい変数にSAVE属性を付けてあげましょう。 コンパイラのオプションで全ての変数をstaticにしてもよいですね。Intelコンパイラの場合は-saveか/Qsaveです。 昔の普通のコンパイラはsave属性がついているのがデフォルトでしたが,今は違います。

関連するQ&A

  • フォートランの配列の受け渡し問題

    いまどき、フォートランの配列について質問します。 配列の実引数(メイン側の配列変数)とサブルーチン側の変数(仮引数の配列変数)の受け渡しですが、配列のサイズを変動させることが可能でしょうか。 program main dimension x(100) ! xの最大配列サイズ100 .... call abc(x,np) ! 実際にはxはnp(<100)個の配列とする。   .... subroutine abc(xx,nx) ! x->xx(仮引数,配列サイズはnx=np) dimension xx(1) ! 配列の先頭を示しておく。 .... こうしておくと、サブルーチンabcで利用する配列は呼び出し側でnp(=nx,=<100)を変更することによってサイズをダイナミックに変動できると思っています。実験すると大丈夫なようです。 では、2次元、3次元の配列では配列のサイズを変数でコントロールするにはどうしたらいいでしょうか。実引数と仮引数で名前以外の変数情報を一致させるというのが教科書的な対応だと思いますが、ダイナミックに変数を変化させるにはどうしたらいいでしょうか。 追加ですが、プログラムに対して外部データを入力させる場合のnamelist入力について解説しているサイトとか本があるでしょうか。これは古臭い(フォートランでさえ古いですが、さらに古い)やり方かと思いますが、解説本には載っていないようです。 以上、よろしくお願いします。 ところで、フォートランの復権なんてことはやはりないのでしょうか。ポインタなども導入されてC言語みたいになってきているとは思いますが。

  • Fortranのサブルーチン引数について

    他人が作ったFortran(恐らくFortran77)のプログラムで計算をしようとしています。 コンパイルはできるのですが,実行するとエラーが出てしまい困っています。 エラーメッセージは 「forrtl:severe(157):Program Exception - access violation」 となっていて,実行時エラー番号で調べると 「プログラムが適切な参照権を持っていない仮想アドレスに書き込み,または読み取り を行おうとした」と出ていました。 Fortran初心者の自分にはイマイチ意味が判らないのですが,エラーが出た後に サブルーチンのある決まった場所に黄色い矢印が示されます。 そこで今,その部分のサブルーチンを確認しているところです。 前置きが長くなってしまいましたが サブルーチンの中を確認している途中で疑問に思った所が一つあります。 問題のサブルーチンは --------------------------------------- call dbesl(EN,0,BX0,BY0,BI0,BK0)     subroutine dbesl(X,NOR,BX,BY,BI,BK) FN = NOR  ・  ・  ・ 11 NOR = FN               <---この行に黄色い矢印が出る return end --------------------------------------- となっているのですが,サブルーチンを呼び出すcall文の所で 2つ目の引数が『0』となっているのは正しい使い方なのでしょうか? どうもサブルーチン内の計算をしてメインプログラムに引数を返す所が おかしいような気がしています。 因みにメインプログラムに戻る際の"FN"の値は『0』となっていました。 コンパイラはCompaq Visual Fortranを使用しています。 足りない情報は出来るだけ追記していきますので,ご教示宜しくお願いします。

  • フォートランの動作に関する質問

    以下のような短いフォートランプログラムがあります。OS:Windows10 module com integer,parameter ::nd=10 end module program main use com print *, nd call sub stop end subroutine sub use com print *, nd return end 内容はモジュールで宣言したパラメータを確認してみるということです。 このプログラムがgfortranで動作せず、intel fortranでは全く問題ありません。 gfortranの結果は以下のようです。 f951.exe: Fatal Error: Reading module 'com' at line 1 column 1: Unexpected EOF GNU Fortran (Rev1, Built by MSYS2 project) 9.3.0 Copyright (C) 2019 Free Software Foundation, Inc. gfortranのこのバージョンに何か問題があるのでしょうか。前からすこしおかしいことには気づいていたのですが。私のコードに問題があるでしょうか。 ※Cが一番近いと思いましてこちらに上げました。

  • サブルーチン、グローバル変数がわかりません。

    2つの行列の計算をサブルーチン関数とグローバル変数を使って行いたいのですが、サブルーチン関数を宣言する為の、プロトタイプ宣言やプロトタイプ定義や、グローバル変数など、 調べてもよく理解できません。 とりあえず、二次元配列を用いたソースコードを書いてみました。 (1)14行目と23行目からのfor文、(2)40行目と51行目と62行目からのfor文を1つにまとめて、最初のプロトタイプ宣言は void 関数(double a[LINE][COLUMN] , double b{LINE][COLUMN] , double c[LINE][COLUMN]) ; にすればいいと思うのですが、 その後は、どのようにすればいいのでしょうか? あと、scanfも使って、aとbも入力できるようにしたいです。 1 #include <stdio.h> 2 3 #define LINE 3 4 #define COLUMN 3 5 6 int main(int argc, char *argv[]) 7 { 8 double a[LINE][COLUMN] ; 9 double b[LINE][COLUMN] ; 10 double c[LINE][COLUMN] ; 11 int i ; 12 int j ; 13 14 for(i= 0;i < LINE ; i++) 15 { 16 for(j = 0 ; j<COLUMN ; j++) 17 { 18 printf("A[%d][%d]=", i+1 , j+1 ); 19 scanf("%f", &a[i][j]); 20 } 21 } 22 23 for(i =0;i < LINE ; i++) 24 { 25 for(j = 0 ; j<COLUMN ; j++) 26 { 27 printf("B[%d][%d]=", i+1 , j+1 ); 28 scanf(" %f", &b[i][j]); 29 } 30 } 31 32 for(i =0;i < LINE ; i++) 33 { 34 for(j = 0 ; j<COLUMN ; j++) 35 { 36 c[i][j] = a[i][j] + b[i][j] ; 37 } 38 } 39 40 printf("\n行列A:\n"); 41 for(i =0;i < LINE ; i++) 42 { 43 for(j = 0 ; j<COLUMN ; j++) 44 { 45 printf("%10.5f" , a[i][j]) ; 46 } 47 48 printf("\n"); 49 } 50 51 printf("\n行列B:\n"); 52 for(i= 0;i < LINE ; i++) 53 { 54 for(j = 0 ; j<COLUMN ; j++) 55 { 56 printf("%10.5f" , b[i][j]) ; 57 } 58 59 printf("\n"); 60 } 61 62 printf("\nC(和):\n"); 63 for(i= 0;i < LINE ; i++) 64 { 65 for(j = 0 ; j<COLUMN ; j++) 66 { 67 printf("%10.5f" , c[i][j]) ; 68 } 69 70 printf("\n"); 71 } 72 return 0 ; 73 }

  • 副プログラムの変数について

    Fortranなのですが、以下のようなプログラムがあります。 call sub1 call sub1 end subroutine sub1 !save a write(*,*) a a=1.0 return end sub1というサブルーチンをメインから2回呼び出すだけです。そのサブルーチンの中の変数aですが、2回目に呼び出されたときに1回目に設定したa=1が保持されているようです。2回目の呼び出しで1回目に設定したa=1に対応した出力になっています。 Fortranはこのような動作をするのでしょうか。save aをコメントアウトしているのです。save aを指定したときだけそうなると思っていたのですが。cなどは細々とした設定ができるだろうと思います。このサンプルコードは実際にそうなっているという実験なのですが、どういう風に解釈したらいいでしょうか。〇〇保存属性とかです。昔からこうだったのでしょうか。使用したのはgfortran ver.9.3ですが。 

  • 隔絶されているサブルーチン間の通信の方法

    以下のようなFortranのサブルーチンとモジュールがあります。これを見ると、サブルーチンA,Bはモジュールa,bが別なのでデータ的に隔絶されていることになります。もし、このような場合、サブルーチンA,Bの間で何らかの変数を共有するようなことをしたい場合、どのような方法があるでしょうか。サブルーチンの引数を使うのは混乱の原因になりそうなので、できればmoduleの方で処理できないかと思うのですが。 混乱しそうなところなので、やり方をいろいろ比較して見てみたいと思います。Cではグローバル変数のように全体で共有する変数を用意するのかなと思いますが。 module a end module module b end module subroutine A use a end subroutin B use b end Fortranについてはここでは特設会議室はありませんが、どこかいいところがあるでしょうか。Cの専門家はいろんなことに通じていると思うのでここにお尋ねしました。よろしくお願いします。

  • プログラムでの配列の渡し方

    ここではフォートランの問題としてお尋ねします。(フォートランのカテゴリがないのでプログラムの専門家に聞けそうなカテゴリに質問してます。) dimension a(100,100) ... call abc(a) ... stop end subroutine abc(b) dimenison b(100,1) ... return end というようなプログラムがあります。メインの方でa(100,100)と宣言してサブルーチンではb(100,1)で受けるというような処理です。 このようにメインとサブで配列のサイズが異るのはどのようなレベルで許容されるものでしょうか。考え方がわからないのでお尋ねします。私は厳密にサイズを合わせると思っていました。しかしそうだとサブルーチンの使い回しができなくなります。このような問題はC言語の配列の先頭のアドレス云々という問題と似ているのですが。2次元配列ということがわかっていて片方のサイズがわかると自ずからもう片方のサイズがわかるということなのでしょうか。 まとめますと、以下のような点がわからないということです。 1.メインとサブで配列サイズが異なっても問題ないやり方 2.サブ側が動的に対応できるようにするプログラムの書き方(同じサブルーチンだけど、呼び出すメイン側の配列サイズに自動で対応する方法) *などを使うのだろうと思いますが。 この部分はしっかり理解しないと大怪我するところなので確認したいと思っています。 (実験して試すというのではなく、仕様としてどうかということですが) よろしくお願いします。

  • サブルーチンでの加算

    CASLIIの課題で、 主プログラムで、連続したN語の領域に格納されているデータの先頭番地をサブルーチンに渡し、サブルーチンでデータを加算し、主プログラムのANS番地に格納するプログラムを作成しなさい。 という問題が出ました。 サブルーチンでの加算法がよくわかりません。 どなたかご教示ください。 MAIN     START LAD GR0,TBL ST GR0,PARA LAD GR1,1 LD GR0,N ST GR0,PARA,GR1 LAD GR1,PARA CALL  SUBR ST GR0,ANS RET TBL DC 1,2,3,4,5,6,7,8,9,10 N DC 10 PARA DS 2 ANS DS 1 END

  • fortranとVBAのdo文

    fortranをBVAのプログラムに直す作業をしています。 プログラムが不慣れなもので、基本的なことかもしれませんが書き込みます。 fortranにて do 10 i=1, n call SET(IX, i, x(i)) 10 continue ここでn、IXは整数で、サブルーチンSETで作業はします。 これをVBAで書き直すとどのようにあらわされるのでしょうか。 よろしくお願いします。

  • フォートランの初歩でつまってます;;

    fortran77入門 浦昭二著 で、いくつか整数を読み込み、その平均値と標準偏差を求める問題があるのですが、コマンドプロンプト上でどのようにしたら計算をしてくれるのかわかりません。例えば5個の整数の和を計算したいなら、5個の整数を入力したあと、何を入力すれば計算してくれるのでしょうか? 初歩的な質問ですいません。 ----------------------------------------------- INTEGER N REAL X,T1,T2,M,SD N=0 T1=0 T2=0 10 READ(5,*,END=20) X N=N+1 T1=T1+X T2=T2+X*X GO TO 10 20 IF(N.GE.2) THEN M = T1/N SD=SQRT((T2-T1*M)/(N-1)) WRITE(6,*) 'N= ',N, ' MEAN = ',M,'SD = ',SD ELSE IF(N.EQ.1) THEN WRITE(6,*) 'N= ',N, ' MEAN = ',M,'SD = ',SD ELSE WRITE(6,*) 'SUCHI GA NAI' END IF END IF END

専門家に質問してみよう