Fortran プログラミングガイド |
第 11 章
C と Fortran のインタフェース
この章では、Fortran と C との相互運用性の問題を取り上げます。
Sun FORTRAN 77、Fortran 95、および C コンパイラに固有な問題だけを扱います。
注 - Sun FORTRAN 77 と Fortran 95 に共通の内容は、FORTRAN 77 を使用して説明します。
互換性について
ほとんどの C と Fortran のインタフェースでは、次に示すことを正しく理解しておく必要があります。
- 関数とサブルーチン: 定義と呼び出し
- データ型: 型の互換性
- 引数: 参照渡しか値渡しか
- 引数: 順番
- 手続き名: 大文字、小文字、末尾の下線 (_)
- ライブラリ: Fortran ライブラリを使用するようリンカーに指示
また、一部の C と Fortran のインタフェースでは、次に示すことを正しく理解しておく必要があります。
関数とサブルーチン
「関数」という言葉の意味は、C と Fortran では異なります。状況によって、どちらの意味で解釈するかが重要です。
- C では、すべての副プログラムが関数です。それらの中には、NULL (
void
) 値を返すものも含めます。- Fortran では、関数とは値を返すものであり、値を返さないものはサブルーチンといいます。
Fortran ルーチンから C 関数を呼び出す場合
C 関数から Fortran 副プログラムを呼び出す場合
- Fortran 副プログラムが関数の場合は、C から、対応するデータ型を返す関数として呼び出します。
- Fortran 副プログラムがサブルーチンの場合は、C から
int
(これは Fortran のINTEGER*4
に対応します) またはvoid
を返す関数として呼び出します。Fortran のサブルーチンが選択戻りをする場合は 1 つの値が戻されます。この場合、RETURN
文にある式の値です。RETURN
文に式がない場合、または SUBROUTINE 文で選択戻りが 宣言されている場合、ゼロが戻されます。データ型の互換性
表 11-1 と表 11-2 で、FORTRAN 77 と Fortran 95 のデータ型のサイズとデフォルトの境界整列を示しています。次の点に注意してください。
- C のデータ型
int、long int、long
は同じです (4 バイト)。64 ビット環境で、-xarch=v9
またはv9a
でコンパイルすると、long
とポインタは 8 バイトになります。これは 「LP64」 で示されます。REAL*16
とCOMPLEX*32
(REAL
(KIND=16)
とCOMPLEX
(KIND=16)
) は、SPARC 上でのみ利用可能です。64 ビット環境で、-xarch=v9
またはv9a
でコンパイルすると、境界は 16 バイト境界になります。- SPARC に関して 4 / 8 と示されている境界整列は、デフォルトでは 8 バイト境界だが、共通ブロックでは 4 バイト境界であることを意味しています。共通ブロックでの最大境界整列は 4 バイトです。
- 配列と構造体の要素および欄はそれぞれ互換性がなければいけません。
- 配列、文字列、構造体を値で渡すことはできません。
f77
から C へ引数を値で渡すことはできます。しかし、C からf77
へはできません。これは、%VAL()
が Fortran 仮引数の並びでは使用できないからです。FORTRAN 77 とC のデータ型
表 11-1 に、FORTRAN 77 のデータ型のサイズと境界を示します。ここでは、境界に影響したり、適用されるデフォルトのデータサイズを昇格させたりするコンパイルオプションを指定しないものとします。(『FORTRAN 77 言語リファレンス』も参照)
SPARC: Fortran 95 と C のデータ型
次の表に、Fortran 95 と C のデータ型の比較を示します。
大文字と小文字
C と Fortran では、字種 (大文字/小文字) に関する扱いが異なっています。
f77
とf95
のデフォルトでは、副プログラム名を小文字に変換して、字種を無視します。実際には、文字列定数の中を除き、すべての大文字を小文字に変換します。大文字と小文字に関する問題には、一般に次のような 2 つの解決策があります。
- C の副プログラムで、C の関数名をすべて小文字にします。
-U
オプションを付けて Fortran プログラムをコンパイルします。これは、f77
に、関数名と副プログラム名における既存の字種の区別をそのまま保持させるオプションです。上記 2 つの解決策のどちらか 1 つを使用してください。両方を使用してはなりません。
この章の例のほとんどは、C の関数名に小文字を使用しています。
f77
の-U
コンパイラオプションは使用していません。ルーチン名の下線
Fortran コンパイラは通常、入口定義と呼び出しの中の両方に現れる副プログラムの名前に下線 (_) を追加します。これによって、ユーザー割り当て名が同じである C の手続きや外部変数と区別します。名前がちょうど 32 文字である場合は、下線は追加されません。Fortran ライブラリの手続き名にはすべて、ユーザーが割り当てるサブルーチンとの競合を減らすため、先頭に 2 つの下線が付けられています。
下線に関する問題には、一般に次の 3 つの解決策があります。
- C の関数で、下線を追加して関数名を変更します。
C()
プラグマを使用して、FORTRAN コンパイラに末尾の下線を省かせます。f77
とf95
で-ext_names
オプションを指定して、下線を使用しないで外部名への参照をコンパイルします。この章の例は、
C()
コンパイラプラグマを使用して、下線を無くしています。C()
プラグマ指令は、外部関数の名前を引数として取ります。これは、このような関数が C 言語で書かれていることを示します。このため、Fortran コンパイラは、通常は外部名に対して追加する下線を、当該の関数名には追加しません。特定の関数に対するC()
指令は、その関数への最初の参照より前に指定しなければなりません。また、そのような参照を含む副プログラムごとに指定しなければなりません。使用規則は次のとおりです。
EXTERNAL ABC, XYZ !$PRAGMA C( ABC, XYZ )このプラグマを使用する場合は、C の関数のほうでは名前に下線を追加してはなりません。
PRAGMA
指令については、『Fortran ユーザーズガイド』で説明しています。引数の参照渡しと値渡し
一般的には、Fortran ルーチンは引数を参照で渡します。呼び出し時に、引数を
f77
およびf95
の非標準関数の%VAL()
で囲んだ場合は、呼び出し元のルーチンはその引数を値で渡します。一般的には、C は引数を値で渡します。引数の前にアンパサンド記号 (
&
) を付けた場合は、C はその引数をポインタを使用して参照で渡します。配列と文字列に関しては、C でも常に参照で渡します。引数と順番
文字列の引数の場合を除くと、Fortran と C は引数を同じ順序で渡します。ただし、各文字列引数については、Fortran ではさらに文字列の長さを示す引数も渡します。文字列長は、値で渡される C の
long int
の量と同じです。
CHARACTER*7 SINTEGER B(3)...CALL SAM( S, B(2) ) char s[7];long b[3];...sam_( s, &b[1], 7L ) ;
配列の添字付けと順番
配列の添字付けと順番については Fortran と C とでは異なります。
配列の添字付け
C の配列は常にゼロから始まりますが、Fortran の配列はデフォルトでは 1 から始まります。この問題には、次のような 2 つの解決策があります。
- 前述の例のように、Fortran のデフォルトを使用します。このときは、Fortran の
B(2)
要素は C のb[1]
要素と同義になります。- Fortran の配列
B
をB(0)
で始まるように指定します。
INTEGER B(0:2)
- このときは、Fortran の要素
B(1)
が C のb[1]
要素と同義になります。配列の順番
Fortran の配列
A(3,2)
は列主導の順番で、次のように格納されます。
A(1,1) A(2,1) A(3,1) A(1,2) A(2,2) A(3,2)C の配列
A[3][2]
は行主導の順番で、次のように格納されます。
A[0][0] A[0][1] A[1][0] A[1][1] A[2][0] A[2][1]これは、1 次元配列では問題ありません。2 次元以上の配列では、すべての参照と宣言における添字の順番と使用法に気をつけてください。なんらかの調整が必要になります。
たとえば、行列操作の一部を C で行い、残りを Fortran で行うのは混乱が生じる可能性があります。一方の言語で全体の配列をルーチンに渡し、そのルーチン内ですべての行列操作を実行すれば、混乱を避けることができます。
ファイル記述子と
stdio
Fortran の入出力チャネルは、装置番号で表されます。入出力システムは、装置番号ではなく、ファイル記述子を扱います。Fortran の実行時のシステムが装置番号からファイル記述子に変換するので、ほとんどの Fortran プログラムはファイル記述子を認識する必要はありません。
C プログラムの多くは、標準入出力 (
stdio
) と呼ばれるサブルーチンセットを使用しています。Fortran の入出力関数の多くは標準入出力を使用しており、これはオペレーティングシステムの入出力呼び出しを使用しています。このような入出力システムの特性の一部を表 11-3 に示します。
ファイルのアクセス権
C プログラマは一般的に、入力ファイルを読み取り用に開き、出力ファイルを書き込み用または読み書き両用に開きます。
f95
プログラムはファイルをREADONLY
(読み取り専用) としてOPEN (開く) できますが、READWRITE='READ'
、'WRITE'
、'READWRITE'
でファイルを開くこともします。f90
はREADWRITE
指示子はサポートしますが、READONLY
はサポートしません。Fortran は、まず読み書き両用として、それが不可能なときは個別の目的のものとして、可能な限りのアクセス権でファイルを開こうとします。
これは透過的に行われるので、ユーザーが意識するのは
READ、WRITE、ENDFILE
の操作を実行しようとしたがアクセス査権がなかった場合のみです。この一般的な自在性の中で磁気テープの操作は例外です。これは、ファイルに書き込み権があっても、テープに書き込みリングが付いていなければ書き込めないからです。ライブラリと
f77
またはf95
コマンドでのリンク適切な Fortran および C ライブラリをリンクするためには、
f77
またはf90
コマンドを使用して、リンカーを起動してください。
demo% cc -c RetCmplxmain.cdemo%f77
RetCmplx.f RetCmplxmain.o このコマンド行でリンクしているdemo% a.out4.0 4.58.0 9.0demo%Fortran 初期化ルーチン
f77
とf95
によりコンパイルされた主プログラムでは、プログラム起動時にライブラリのダミー初期化ルーチンf77_init
またはf90_init
を呼び出します。ライブラリのルーチンは、ダミーであり、何も処理しません。コンパイラにより生成される呼び出しでは、プログラムの引数と環境へのポインタが渡されます。これらの呼び出しにより、プログラマが各自の C 言語ルーチンでプログラムの起動前に独自の方法でプログラムを初期化するときに使用できるソフトウェアのフックが提供されます。このような初期化ルーチンの使用法の一つに、国際化された Fortran プログラムに対して
setlocale
を呼び出す方法があります。Setlocale
は、libc
が静的にリンクされている場合機能しないので、libc
に動的にリンクされる Fortran プログラムだけが国際化できます。ライブラリの
init
ルーチンのソースコードは、次のとおりです。
void f77_init(int *argc_ptr, char ***argv_ptr, char ***envp_ptr) {}void f90_init(int *argc_ptr, char ***argv_ptr, Char ***envp_ptr) {}ルーチン
f77_init
は、f77
主プログラムにより呼び出されます。ルーチンf90_init
は、f95
主プログラムにより呼び出されます。各引数には、argc
のアドレス、argv
のアドレス、envp
のアドレスが設定されます。データ引数の参照渡し
Fortran ルーチンと C 手続きとの間でデータを渡す標準的な方法は、参照渡しです。C の手続きから見ると、Fortran の副プログラムまたは関数呼び出しは、すべての引数をポインタで表す手続き呼び出しのようになります。唯一異なる点は、Fortran が文字列と関数を引数としてあつかう方法と、
CHARACTER*
n 関数の戻り値としてあつかう方法です。単純なデータ型
- 単純なデータ型の場合 (
COMPLEX
またはCHARACTER
文字列以外)、次に示すように、C ルーチンにおいてそれぞれ関連する引数をポインタにより定義するか、または渡します。
複素数データ
- Fortran の複素数データ項については、2 つの
float
または 2 つのdouble
からなる 1 つの C の構造体へのポインタとして渡します。
64 ビット環境で、
-xarch=v9
でコンパイルすると、COMPLEX
値がレジスタに戻されます。文字列
C と Fortran ルーチンとの間で文字列を渡すことは推奨できません。これは、標準的なインタフェースがないからです。ただし、次を考慮してください。
- すべての C 文字列は参照で渡される。
- Fortran の呼び出しは、引数リストにある
character
型の全ての引数についてそれぞれもう 1 つの引数を渡します。この追加引数は、文字列の長さを渡すもので、値で渡される C のlong int
と同じです (実装により異なります)。この文字列の長さを渡す追加引数は、呼び出しの明示的に指定した引数の後に現れます。次の例で、文字列を引数とする Fortran 呼び出しを、対応する C のコードと共に示します。
表 11-6 CHARACTER 文字列を渡す CHARACTER*7 S
INTEGER B(3)
...
CALL CSTRNG( S, B(2) )
...
char s[7];
int b[3];
...
cstrng_( s, &b[1], 7L );
...
文字列の長さが呼び出されたルーチンで必要なければ、追加の引数は無視されます。ただし、Fortran では C のように明示的なヌル文字で文字列を自動的に終了させません。これは、呼び出し側のプログラムで追加する必要があります。
1 次元配列
- C では配列の添え字が 0 で始まります。
2 次元配列
- C と Fortran とでは行と列が入れ替わります。
構造体
- C と FORTRAN 77 構造体および Fortran 95 の構造型については、対応する成分に互換性があるかぎり、それぞれのルーチンに渡すことができます。
ポインタ
- FORTRAN 77 の
pointer
を、ポインタへのポインタとして C のルーチンへ渡すことができます。これは、Fortran ルーチンが引数を参照で渡すからです。
- C ポインタは Fortran 95 のスカラーポインタとは互換性がありますが、配列ポインタとは互換性がありません。
データ引数の値渡し
値による呼び出しは、FORTRAN 77 で単純型データでだけ利用でき、また、C のルーチンを呼び出す Fortran ルーチンによってだけ利用できます。C のルーチンが Fortran ルーチンを呼び出して、値により引数を渡す方法はありません。配列、文字列、構造体を値で渡すことはできません。参照により渡すようにしてください。
- 非標準の Fortran 関数
%VAL
(引数) を呼び出しの中で引数として使用します。次の例では、Fortran ルーチンが値により
x
を、参照によりy
を渡しています。C のルーチンはx
とy
を増分しましたが、y
だけが変更されます。
値を戻す関数
BYTE
(FORTRAN 77 のみ)、INTEGER、REAL、LOGICAL、DOUBLE PRECISION
またはREAL*16
(SPARC のみ) 型の値を戻す Fortran 関数は、互換性のある型を戻す C の関数と同義です (表 11-1 と表 11-2 を参照)。文字関数の戻り値には引数が 2 つ追加され、複素数関数の戻り値には引数が 1 つ追加されます。単純型データを戻す
- 次の例は
REAL
またはfloat
値を戻します。BYTE、INTEGER、LOGICAL、
および
DOUBLE PRECISIONREAL*16
も同じような方法であつかわれます。
複素数データを戻す
COMPLEX
またはDOUBLE COMPLEX
を戻す Fortran 関数は、メモリーにある戻り値を指す追加の引数を最初の引数として持つ C の関数と同じです。Fortran 関数と対応する C 関数の一般的な形式は次のとおりです。
COMPLEX FUNCTION CF(a1, a2, ..., an) cf_ (return, a1, a2, ..., an)struct { float r, i; } *return;
64 ビット環境で、
-xarch=v9
でコンパイルすると、COMPLEX
値が浮動小数点レジスタに戻されます。COMPLEX
とDOUBLE
COMPLEX
はそれぞれ %f0 と%f1 に、COMPLEX*32
は %f、%f1、%f2、%f3 に戻されます。これらのレジスタは C プログラムでは直接アクセスできないので、この場合の SPARC V9 プラットフォームでは Fortran と C 言語の間で相互操作性は実現されません。
CHARACTER
文字列を戻す
- C と Fortran ルーチンの間で文字列を渡すことは推奨できません。ただし、Fortran の文字列の値を持つ関数は、データアドレスとデータ長の 2 つの引数がはじめに追加された C の関数と同じです。Fortran 関数と対応する C 関数の一般的な形式は次のとおりです。
CHARACTER*n FUNCTION C(a1, ..., an) void c_ (result, length, a1, ..., an)char result[ ];long length;
この例では、C 関数と呼び出し側の C ルーチンは、リストの最初に 2 つの引数 (結果として戻される文字列へのポインタと文字列長) が、そして、リストの最後に 1 つの追加引数 (文字列引数の長さ) が追加されています。C から呼び出された Fortran ルーチンでは最後のヌル文字を明示的に追加する必要があることに注意してください。
名前付き
COMMON
Fortran の名前付き
COMMON
は、大域的構造体を使用して C の中で代替できます。
表 11-16 名前付き COMMON
COMMON /BLOCK/ ALPHA,NUM
...
extern struct block {
float alpha;
int num;
};
extern struct block block_ ;
main ()
{
...
block_.alpha = 32.;
block_.num += 1;
...
}
C ルーチンにより確立された外部名は、Fortran プログラムにより作成されたブロックとリンクさせるために下線で終了しなければなりません。C 指令
#pragma pack
は Fortran のときと同じ埋め込みが必要な場合があります。f77
とf95
の両方で、共通ブロックのデータを最大で 4 バイト境界に境界割り当てします。Fortran と C との入出力の共有
Fortran の入出力と C の入出力を混合することは (C と Fortran ルーチンの両方から入出力呼び出しを発行すること)、推奨できません。すべて Fortran の入出力で行うか、すべて C の入出力で行うかのどちらかに統一するのが安全です。
Fortran の入出力ライブラリは、大部分が C の標準入出力ライブラリの上に実装されています。Fortran プログラムで開いた装置はすべて、標準入出力のファイル構造と対応付けられています。
stdin、stdout、stderr
のストリームに関しては、ファイル構造を明示的に参照する必要がなく、共有できます。Fortran の主プログラムが入出力を行うために C を呼び出す場合、Fortran の入出力ライブラリはプログラム起動時に、装置 0、5、6 がそれぞれ
stderr、stdin、stdout
に接続するよう初期化されます。ファイル記述子を開いて入出力を実行する場合、C の関数は Fortran の入出力環境を考慮しなければなりません。ただし、C の主プログラムが Fortran の副プログラムを呼び出して入出力を行う場合、装置 0、5、6 を
stderr、stdin、stdout
に接続する Fortran 入出力ライブラリの自動初期化が行われません。この接続は通常 Fortran の主プログラムにより行われます。Fortran 関数が通常の Fortran 主プログラム入出力初期化をせずにstderr
ストリーム (装置 0) を参照しようとした場合、出力はstderr
ストリームではなく、fort.0
に書き込まれます。C の主プログラムは、プログラムの先頭で FORTRAN 77 のライブラリルーチン
f_init()
を呼び出すことにより Fortran 入出力を初期化し、装置 0、5、6 を事前に接続することができます。また、必要に応じて終了時にf_exit()
を呼び出すこともできます。たとえ主プログラムが C で書かれていても、
f77
でリンクするべきであることに注意してください。選択戻り
Fortran の選択戻り機能はあまり使用されなくなっているため、移植上の問題がなければ使用しないでください。選択戻りに対応する機能は C にはありません。したがって、問題は C のルーチンが選択戻りを持つ Fortran のルーチンを呼び出す場合だけです。
Sun Fortran では、
RETURN
文にある式はint
の値を戻すようになっています。これは実装方式にかなり依存するため、使用しないでください。
サン・マイクロシステムズ株式会社 Copyright information. All rights reserved. |
ホーム | 目次 | 前ページへ | 次ページへ | 索引 |