ホレリス定数とは
FORTRAN66 には現代的な意味での文字型はなく、かなり厳しい制約がありました。
- 整数型変数に文字コードを埋め込んで使う(文字数の上限は処理系依存)
- 「文字型」定数が書ける場所は DATA 文と CALL 文だけ
FORTRAN 66 の文字型定数は「字数 H
値」という異様な形式です。たとえば 1HX
が FORTRAN77 の 'X'
に相当し、4HABCD
が 'ABCD'
に相当するといった具合です。FORMAT 文をよく知っている人は「H型編集記述子」と同じ形だと気づかれるでしょう。読み書きしやすいものではないので、後の FORTRAN77 以後では引用符によるほうが文字列定数と呼ばれ、「字数 H
値」の形式をホレリス定数と呼びます。
DATA 文の例です。
C 型宣言がなくても KSYM は整数型になる
INTEGER KSYM
DATA KSYM/4HABCD/
これで KSYM
という変数ができて(整数型ですから変数名は普通 IJKLMN で始まる名前が使われます)、'ABCD'
の文字コードにあたる値が入るわけです。整数型変数ですから整数として使えるわけですが、リトルエンディアン機では 0x44434241 が入ることになります。
CALL 文の例です。
CALL SUB(4HABCD, 他の引数...)
この例では、あたかも上のDATA文があったかのように整数型の値が作られ、それが引数として渡されます。
単純に文字型定数で置き換えてはいけない
ホレリス定数の代わりに(FORTRAN77以後の引用符による)文字列定数が作られたのですから、文字列定数で置き換えてしまいたいですよね。CALL SUB(4HABCD, 他の引数...)
とする代わりに CALL SUB('ABCD', 他の引数...)
としたくなります。
ところがこれが駄目なんです。
FORTRANの大抵の実装では、サブルーチンに入るときに数値型であればその変数のポインタをスタックに積みますが、文字型であれば文字データへのポインタを積むだけでなく、すべての引数をスタックに積んだあとで文字列長をスタックに積みます。最近は先頭数個の引数をスタックじゃなくてレジスタに置いたりしますが原則は変わりません。
ホレリス定数は整数型ですから、文字型の引数を渡してしまうと、文字列長が余計にスタックに積まれてしまうわけですね。特にホレリス定数の引数のあとに文字型の引数がある場合(長期間保守され続けてきたコードにはままある)はほぼ確実に予期しない動作となります。
(上記の説明から「動いてしまう場合」がわかる人は自己責任で。悪いことはいわんからやめておけ)
整数型変数に文字コードを入れる対処
ホレリス定数を受けるサブルーチンを変更しない場合は、整数型変数に文字コード値を入れることになります。
最近知ったのですが古いコンパイラ(少なくともIntel Fortran)ではそのものズバリ代入できてしまうようです。でも gfortran では通りません。
C <非推奨>古いコンパイラでは使えるが gfortran では通らない
KSYM = 'ABCD'
Fortran90 以後のお作法では、TRANSFER 関数を使います。
C <推奨>Fortran90 対応ならばすべてのコンパイラで使える
C 文字列 'ABCD' のビット列を KSYM に転写
KSYM = TRANSFER('ABCD', KSYM)
文字コードというと、FORTRAN77 の教科書で ICHAR 関数が使えないのかと考える人がいるかもしれません。実は文字列長が1の場合に限り、X86などリトルエンディアン機では、次のコードが動いてしまいます。数値的下位ビットが、記憶列での下位アドレス(記憶列の先頭)に来るからですね。でもビッグエンディアン機では動かないので、これもまた自己責任でお願いします。
C <非推奨>リトルエンディアン機で1文字に限り動作
KSYM = ICHAR('A')
追記:逆にホレリス定数を受けるサブルーチンは
蛇足ですが、上と同じ理由で、ホレリス定数を受けるサブルーチンを脱ホレリスしようとするとき、呼び出し側を変えないで(多数あって変えられない場合など)無闇に文字型に書き換えてはいけません。
SUBROUTINE SUB(KSYM, 他の引数...)
C 型宣言がなくても KSYM は整数型になる
INTEGER KSYM
とあったら
C 呼び出し規約上非互換になります
SUBROUTINE SUB(KSYM, 他の引数...)
CHARACTER(4):: KSYM
にすると余計にスタックを掘りにいって最悪SEGVします。
このようにして文字型に変換することで、互換の動作になります。
SUBROUTINE SUB(KSYM, 他の引数...)
C 型宣言がなくても KSYM は整数型になる
INTEGER:: KSYM
CHARACTER(4):: CSYM
C KSYM のビット列を CSYM に転写
CSYM = TRANSFER(KSYM, CSYM)