Fortran 95/90 では,配列を配列要素として,部分配列として,または配列名で引用される配列全体として渡すことができます。Fortran 95/90 の中では,配列要素は列主順で並べられており,最も下の次元の添字が先に変化します。
Fortran と他の言語の間で配列を使用するときには,要素の索引付けと順序を考慮に入れる必要があります。配列要素は個別に引用し,追跡しなくてはなりません。Fortran,Visual Basic,MASM,および C では,配列要素の索引付けの方法が異なります。配列の索引付けはソース・レベルでの問題であり,下位のデータに違いはありません。
Visual Basic は配列と文字列を記述子,すなわち配列の大きさと位置を含んでいるデータ構造として格納します。ただし,この記憶形態の違いはユーザーからは見えません。
Visual Basic から Fortran に配列を渡すには,配列の最初の要素を渡します。基本設定では,Visual Basic は変数を参照によって渡すので,配列の最初の要素を渡すと,Fortran には Fortran が期待しているとおりに,配列の開始位置が与えられます。Visual Basic は基本設定では最初の配列要素の索引を 0 としますが,Fortran は基本設定では 1 とします。次の文を使えば,Visual Basic の索引付けを 1 から開始させることができます。
Option Base 1
別の方法として,どちらの言語でも,配列宣言中で,配列の下限を -32,768 ~ 32,767 の範囲の任意整数に設定することができます。次に例を示します。
Basic コード:
Declare Sub FORTARRAY Lib "fortarr.dll" (Barray as Single) DIM barray (1 to 3, 1 to 7) As Single Call FORTARRAY(barray (1,1))
Fortran コード:
Subroutine FORTARRAY(arr) REAL arr(3,7)
MASM では,配列は 1 次元であり,配列要素はバイト単位に引用しなくてはなりません。機械語は配列の要素をメモリー内に連続して格納し,最初のアドレスが配列名によって引用されます。個々の要素を参照するには,最初の要素を基準として,目的の要素の前にある要素の合計バイト数を読み飛ばします。次に例を示します。
xarray REAL4 1.1, 2.2, 3.3, 4.4 ; initializes ; a four element array with ; each element 4 bytes
MASM での xarray の引用は最初の要素,すなわち 1.1 を含んでいる要素を引用します。第 2 の要素を引用するには,最初の要素の 4 バイト後にある要素を,xarray[4] または xarray+4 で引用する必要があります。次に別の例を示します。
yarray BYTE 256 DUP ; establishes a ; 256 byte buffer, no initialization zarray SWORD 100 DUP(0) ; establishes 100 ; two-byte elements, initialized to 0
Fortran と C の配列は 2 つの点で異なります。
配列の下限の値が違います。基本設定では,Fortran は配列の第 1 要素の索引を 1 とします。C と Visual C++ は 0 とします。このため,Fortran の添字は 1 だけ大きい値にしなくてはなりません。Fortran には,下限として別の整数を指定するオプションもあります。
2 次元以上の配列では,Fortran は最も左の索引を最も速く変化させるのに対し,C は最も右の索引を最も速く変化させます。これは,それぞれ列主順と行主順と呼ばれることがあります。
C では,X[3][3] として宣言されている配列の最初の 4 要素は次のようになっています。
X[0][0] X[0][1] X[0][2] X[1][0]
一方,Fortran では,最初の 4 つの要素は次のようになっています。
X(1,1) X(2,1) X(3,1) X(1,2)
索引の順序は,次元をいくつ宣言しても同じです。たとえば,次の C の宣言を考えます。
int arr1[2][10][15][20];
これは Fortran の次の宣言と等価です。
INTEGER arr1( 20, 15, 10, 2 )
C の配列宣言に使用される定数は,他の言語のように上限ではなく,範囲を表しています。このため,int arr[5][5] と宣言された C 配列の最後の要素は,arr[5][5] ではなく arr[4][4] です。
次表に,配列宣言の対応関係を示します。
各種言語の配列宣言の対応関係
言語 | 配列宣言 | Fortran からの配列引用 |
Fortran | DIMENSION x(i, k) または type x(i, k) |
x(i, k) |
Visual Basic | DIM x(i, k) As type | x( i - 1, k - 1 ) |
Visual C/C++ | type x[ k ] [ i ] | x( i - 1, k - 1 ) |
MASM | 配列は連続した記憶域にある要素として宣言し引用する。 |
Visual Fortran の配列記述子書式
Fortran 95/90 が複数のポインタ・メモリー・アドレスを追跡しなければならない場合に備えて,Visual Fortran コンパイラは,配列の編成の詳細を格納する配列記述子を使用します。
(結合または手続インタフェース宣言による) 明示的なインタフェースを使用する場合,Visual Fortran は次の形式の配列引数記述子を生成します。
配列へのポインタ (配列ポインタ)
形状引継ぎ配列
一部のデータ構造引数は,適切な明示的インタフェースが提供されている場合でも記述子を使用しません。たとえば,明示的整形配列と大きさ引継ぎ配列は記述子を使用しません。一方,配列ポインタと割付け配列は,引数として使われるかどうかにかかわらず記述子を使用します。
Visual Fortran と Fortran 以外の言語 (C など) の間で呼び出しを行う場合,暗黙のインタフェースを使用すれば,配列を Visual Fortran 記述子なしで渡すことができます (「配列引数の効率的な渡し方」を参照)。ただし,呼び出されるルーチンが Visual Fortran 記述子に含まれる情報を必要とする場合,ルーチンを明示的なインタフェースを付けて宣言し,仮配列を形状引継ぎ配列として,またはポインタ属性を付けて指定します。
Fortran 95/90 ポインタは,(配列境界が「矩形」である限り) 任意の形に編成された任意のメモリー部分と関連付けることができます。また,Fortran 95/90 ポインタを C などの他の言語に渡し,その言語で記述子を正しく解釈して,必要な情報を取得することができます。
ただし,配列記述子を使用すると,エラーが起こる可能性が高まりますし,可搬性も損なわれます。
記述子が正しく定義されていないと,プログラムは誤ったメモリー・アドレスを参照し,一般保護違反を発生させることがあります。
配列記述子の書式は,個々の Fortran コンパイラに固有です。配列記述子を使用するコードには,他のコンパイラやプラットフォームへの可搬性はありません。たとえば,(Win32 システムの) Visual Fortran の配列記述子書式は,True64 UNIX と OpenVMS システムの Compaq Fortran の配列記述子書式とは異なります。Visual Fortran の配列記述子書式は Microsoft Fortran PowerStation で使用されている書式と同じです。
配列記述子書式は,将来変更される可能性があります。
次に,Visual Fortran の配列記述子の構成要素を示します。
第 1 ロングワード (バイト 0 ~ 3) は,ベース・アドレスを含んでいます。ベース・アドレスにオフセットを加えたものが,配列の最初のメモリー位置 (始点) を定義します。
第 2 ロングワード (バイト 4 ~ 7) は,配列の 1 つの要素の大きさを含んでいます。
第 3 ロングワード (バイト 8 ~ 11) は,オフセットを含んでいます。オフセットをベース・アドレスに加えることで,配列の始点が定義されます。
第 4 ロングワード (バイト 12 ~ 15) は,配列が定義されている (記憶域が割り当てられている) 場合に設定される下位ビットを含んでいます。
第 5 ロングワード (バイト 16 ~ 19) は,配列の次元 (次元数) を含んでいます。
残りのロングワード (バイト 20 ~ 103) は,個々の (7 つまでの) 次元に関する情報を含んでいます。各次元は,さらに 3 つのロングワードによって記述されます。
要素の数(範囲)
2 つの連続する要素の開始アドレスの距離 (バイト数)
下限
次元数 1 の配列では 3 つのロングワードが追加で必要となるので,合計で 8 つのロングワード (5 + 3 * 1) が含まれ,バイト 31 で終わることになります。次元数 7 の配列には,合計で 26 のロングワード (5 + 3 * 7) が含まれ,バイト 103 で終わることになります。
例として,次の宣言を考えます。
integer,target :: a(10,10) integer,pointer :: p(:,:) p => a(9:1:-2,1:9:3) call f(p) . . .
実引数 p の記述子は,次の値を含むことになります。
第 1 ロングワード (バイト 0 ~ 3) は,ベース・アドレスを含みます (実行時に割り当てられます)。
第 2 ロングワード (バイト 4 ~ 7) は,4 に設定されます (1 つの要素のサイズ)。
第 3 ロングワード (バイト 8 ~ 11) は,オフセットを含みます (実行時に割り当てられます)。
第 4 ロングワード (バイト 12 ~ 15) は,1 を含みます (下位ビットが設定されます)。
第 5 ロングワード (バイト 16 ~ 19) は,2 を含みます (次元数)。
第 6,第 7,および第 8 ロングワード (バイト 20 ~ 31) は,最初の次元に関する次のような情報を含みます。
5 (範囲)
-8 (要素間の距離)
1 (下限)
第 9,第 10,および第 11 ロングワード (バイト 32 ~ 43) は,第 2 の次元に関する次のような情報を含みます。
3 (範囲)
120 (要素間の距離)
1 (下限)
この例では,バイト 43 が最後のバイトとなります。
各種の型の配列引数を渡すときの性能に対する影響については,「配列引数の効率的な渡し方」を参照してください。