Navigation : Top/FORTRANとCが混在したプログラム

FORTRANとCが混在したプログラム

FORTRANとCが混在したプログラム

注意事項

  • cから見ると,fortranのサブルーチン”XXX”は,”XXX_”という風にアンダースコア"_"が入って見える.
    これはnm XXX.oで確認できる.
  • 実行ファイルは,各サブルーチンをf77 -c XXX.fとしてオブジェクトファイルを生成し,f77 XXXX.c XXX.oとしてf77を使ってcのメインルーチンをコンパイルする.
  • ccを使用してcのメインルーチンをコンパイルすると,fortranの中のwrite(*,*)についてエラーが出る.
  • 2次元配列の引き渡しは,cの方が1つ余分に宣言しておく.
    例) 3X3行列 
    c: 
    int   dat1[3][3];
    int   nx=3, ny=3
    
    f:
    subroutine matrix(a,nx,ny)
    integer*4  nx,ny
    integer*4  a(nx,ny),b(nx,ny)
    理由は,cの行列は(0-2,0-2)であるが,fortranでは(1-3,1-3)となるため
  • 2次元配列はcとfortranのルーチンの間では転地行列となるのでチュウイが必要
    例) 
    **************************************************************************
    転置行列を計算 - CとFortran間での2次元配列のやりとり
    * -----------------------------------------------------------------------
    * transpose_2dmatrix_i / 整数行列
    * transpose_2dmatrix_i / 実数行列
    * transpose_2dmatrix_i / 倍精度実数行列
    * -----------------------------------------------------------------------
    * Input:
    * a - Matrix with (nx,ny)
    * nx - number of gyo of matrix a
    * ny - number of retsu of matrix a* Output:
    * b - Transpose Matrix of a ** Dec.7, 1998 / Nobuhito Mori 
    *************************************************************************
         subroutine transpose_2dmatrix_i(a,b,nx,ny)  
    *************************************************************************
    * global variables
         integer*4 nx,nyinteger*4 a(nx,ny), b(nx,ny)
    * local variables
         integer*4 c(nx*ny)integer*4 i, j, k
    *========================================================================
         k=0
         do 10 j=1, ny
           do 10 i=1, nx
             k=k+1
             c(k)=a(i,j)
     10  continuek=0
         do 20 i=1,nx
           do 20 j=1,ny
             k=k+1
             b(i,j)=c(k)
     20 continue
        return
        end

Cプログラムの中からFORTRANサブルーチンを呼ぶ

CのプログラムとFORTRANのプログラムを混在して使いたい場合いくつか注意する必要があります。まずCプログラムの中からFORTRANサブプログラムを呼ぶ場合を考えてみましょう。 それぞれの言語の枠の中では関数名や変数名などユーザが定義したとおりに使ってきましたが、異なる言語処理系では実はそのままには見えません。nmコマンドで.oファイルを見れば解かりますが、コンパイラーが自動的に名前を修正します。例えば

       % cat test.c
       int     inc1( i )
       int     i;
       {
               return i + 1;
       }
       % cc -c test.c
       % nm test.o
       00000000 T _inc1
       %

C言語の枠組みではinc1という関数を定義したのですが、オブジェクトモジュールの中には_inc1と、前に_を付けた名前になって保存されています。同じようにFORTRANの場合、

       % cat testf.f
               integer function inc1( i )
               integer i
               inc1 = i + 1
               return
               end
       % f77 -c testf.f
       % nm testf.o
       00000000 T _inc1_

今度は前と後ろに_が付きました。FORTRANの場合自動的に前と後ろに_を付けていることがわかりました。このことからCからFORTRANサブプログラムを呼び出すときには関数名の後に_を付けて呼び出せばよいということになります。 (前に付ける_はCコンパイラが自動的にやってくれます。) 次に考えなければならないのは引数の与え方です。Cの場合は基本的に値を渡しますがF ORTRANの場合はすべて参照を渡します。その結果CからFORTRANを呼ぶ場合すべて引数はポインターで渡してやらなければなりません。上の例で FORTRANのinc1をCから呼び出す場合、

       /* Cプログラムの中 */
       int     i, j;
       i = 10;
       j = inc1_( & i );       /* FORTRAN のinc1を呼んだ */

ということになります。関数の値の戻し方は同じです。このように任意の FORTRANサブプログラムをCから呼ぶことができることがわかります。 CのプログラムとFORTRANプログラムで最も取り扱い方が異なるのは文字列です。Cでは0 (ヌル文字)で終端される任意長さの文字列を扱いますが、FORTRAN では文字列変数は固定長で、代入された長さが変数の長さより短い場合は空白が埋められます。また、文字列の最後にヌル文字を付けてはくれません。ですからstrlen関数では正しい長さが返らないことに注意しましょう。FORTRANの関数lnblnkは文字列変数の最後に埋められた空白の最初の位置を返します。 こういったことに注意しながら文字列を扱う例を見ていきましょう。

       subroutine sub1( str1, str2 )
       character str1*40, str2*80

という関数をCから呼ぶときは

       char str1[ 40 ], str2[ 80 ];
       sub1_( str1, str2 );

というように同じ長さの文字列を宣言しておく必要があります。では次の場合はどうでしょう。

       subroutine sub2( str1, str2 )
       character str1*(*), str2*(*)

固定長なのに長さが指定されていません。この場合はFORTRANプログラムでは見えない隠れた引数が必要になります。Cからは次のように呼び出すことになります。

       char str1[ 40 ], str2[ 80 ];
       sub2_( str1, str2, 40, 80 );

(*)型の引数の分だけ文字列の実際の長さを引数に追加します。かなり苦しい感じがしますね。


FORTRANプログラムの中からCの関数を呼ぶ

では次にFORTRANプログラムからCの関数を呼び出す場合を考えましょう。この場合はCのプログラムの側で配慮をしておかなければならない点がいくつかあります。まず関数名ですがFORTRANコンパイラは自動的に前後に_を付けます。そのためCのプログラムの側でFOR TRANから呼ばれることのある関数名には後ろに_を付ける必要があります。それから引数はアドレスが渡されますからポインター引数を取るように関数を用意する必要があります。上述のinc1を FORTRANコーラブルに書き直しましょう。

       % cat test.c
       /* FORTRANコーラブルなinc1 */
       int     inc1_( pi )
       int     * pi;
       {
               return * pi + 1;
       }

これをFORTRAN側では

       * FORTRANプログラムの中
               integer i
               i = 10
               j = inc1( i )

という風になります。このようにFORTRANから任意のCサブプログラムが呼べるわけではないことがわかります。


FORTRANコモンブロックをCから使う

大規模なFORTRANプログラムではコモンブロックを頻繁に使います。FORTRAN のコモンブロックは言語特有の実装ですがCのプログラムから参照することは可能です。まずCから参照するときのコモンブロックの名前ですが、関数名と同じルールが適用されます。例えば

       * FORTRANプログラムの宣言文
               common /testcb/ k, f
               integer k
               doubleprecision f

この場合コモンブロックは_testcb_という名前が付きます。名前なしコモンの場合は__BL NK__となります。ではコモンブロックのデータを参照するにはどうしたらよいのでしょうか。Cから見るとコモンブロックは一種の構造体に見えます。ですからCのプログラムからこのコモンブロックを参照するには次のような宣言が必要です。

       /* Cプログラムの中 */
       extern struct   {
               int     k;
               double  f;
               }       testcb_;
       ...
       testcb_.k = 10;         /* コモンブロックの要素kに値を代入 */

もちろん構造体testcb_のメンバーとFORTRANのコモンブロック/testcb/の要素の並びは一致していなければなりません。メンバーの名前はどうでもよいのですが。


CとFORTRANの混在したプログラムのリンク

ここまででCとFORTRANの混在したプログラムの書き方を見てきました。次に必要なことはそれらをコンパイルしてリンクすることです。コンパイルはもちろんそれぞれの言語コンパイラで行ないますが、-cを付けてオブジェクトモジュールを作ることは言うまでもありません。これで複数の.oファイルができました。つぎにこれらをリンクして実行可能イメージを作るわけですがここで注意が必要です。

Cのプログラムをリンクする場合UNIXのシステムコールとCのランタイムライブラリが参照されることは以前に説明しました。さらにFORTRANプログラムをリンクする場合そのプログラムがFORTRAN組み込み関数を参照している場合そのライブラリも教えてやる必要があります。Cのランタイムライブラリは /usr/lib/libc.aなどと言う名前で用意されています。FORTRAN組み込み関数は同様にlibF77.aなどという名前になっているはずです。これらのライブラリは ccコマンドやf77コマンドが自動的に取ってきてくれます。ですから、リンクをする場合もldではなくてccやf77を使います。

もう一つ注意する点はプログラムのエントリーポイントのことです。エントリーポイントはC言語の場合man()関数として宣言します。FORTRANの場合 program文でエントリーポイントであることを宣言します。このとき、ユーザが気にしていないところでいくつかの関数や変数が自動的に付加されます。これらの面倒をリンクの時にも見てやらなければなりません。ですから、 FORTRANのエントリーポイントを持ったプログラムを作る場合ccではなくてf77 コマンドでリンクをします。

見てきたようにFORTRANとCそれぞれが非常に重要な役割をはたしていることがわかります。当面はそれぞれの長所を生かした形で混成使用することはやむを得ないでしょう。それぞれの言語の特徴を次にまとめてみますが、どちらも絶対的に優位という訳ではありません。それぞれの括弧の中は反対陣営からの反論です。(別にそういう紛争があるわけではないけど)

  • Cが有利であると思われること
       シェルからの引数がmainで渡せる。コマンド組み込みが自然にできる。        
               (FORTRANでも引数を参照する関数は用意されている)
       システムコールはCから呼ぶように設計されている。
               (FORTRANから呼ぶインターフェースもある)
       構造体やポインターが扱える。
               (FORTRANでも仕様拡張によりできることが多い)
  • FORTRANが有利であると思われること
       CERNLIBはFORTRANからの呼出しを前提に書かれている。
               (Cからだって呼べないことはない)
       一般ユーザはFORTRANの方が使い慣れている。
               (ユーザ定義関数だけFORTRANで書かせることもできる)