69 Fortran と C の混合プログラミング

分からないことを ChatGPT に尋ねて、なるほどーと思ったら、実は間違いだった (正しいことを教えてくれなかった)。 でも、自分の理解は進んだ、という話。

call writec('Hello')
とすると、文字列の長さ5 が暗黙に渡されるので、 writec() が C で書かれた関数の場合、
void writec(char *buf, int len)
のようにして、そうすると len には 5 が入る。 そういう話を読んだ。実際
nantoka.f
       call writec('Hello')
       stop
       end
kantoka.c
#include <stdio.h>

void writec_(char *buf, int len)
{
   printf("%s, len=%d\n", buf, len);
}
% gfortran nantoka.f kantoka.c
% ./a.out
Hello, len=5
%

本当だ。ところが
nantoka2.f
       call writec('Hello',5)
       stop
       end
% gfortran nantoka2.f kantoka.c
% ./a.out
Hello, len=35946216
%
一体全体?? ,5 と書いておくと、それはきっと長さだろう、 じゃあ暗黙に 5 を送るのはやめよう。
kantoka2.c
#include <stdio.h>

void writec_(char *buf, int *len)
{
   printf("%s, len=%d\n", buf, *len);
}
として受け取るべきである。そうすれば
% gfortran nantoka2.f kantoka2.c
% ./a.out
Hello, len=5
となる。

なんじゃ、こりゃあ?? (昭和の人はお分かりになるかと思います。)

この辺で ChatGPT にお尋ねする。

まず文字列をCに渡すならば、NULL 終端にしましょう、と言われる。 それはそうだ (今やろうと思っていたのに)。
       call writec('Hello' // char(0))
       stop
       end
       call writec('Hello' // char(0), 5)
       stop
       end
みたいにするのかな。その上で長さが暗黙に渡されるかどうかは、 処理系依存だそうだ。
✅ 「文字列の長さは暗黙には渡されない可能性が高い」と言った理由
これは まさに処理系依存(=コンパイラ依存)です!

🎯 つまり:
Fortran → C の呼び出しで、リテラル文字列 '...' を引数に渡すとき、

それに対して Fortran が暗黙に長さ引数を追加するかどうかは、

引数の順番(最後かどうか)や、

他の引数の型や数(実数が続いている、明示の長さがある)などに依存し、

コンパイラの実装によって異なる可能性があります

✅ よって、実用上の原則
「文字列の長さを C に渡して使いたいなら、自分で明示的に渡せ!」
「C 側で printf 的に使うなら、Fortran 側で明示的に null 終端せよ!」

これが一番安全でポータブルな方針です。

🛡 まとめ
call cfunc('a=%f', a) のようなコードでは、 Fortran が暗黙に長さを渡すとは限らない(=処理系依存)。

そのため、リテラルには // char(0) を付けて NULL 終端するのが安全。

あるいは character*X の変数にして長さを Fortran → C に明示的に渡すのも手。

ポータビリティや予測可能性を高めるためには、暗黙に頼らず明示で書くのが鉄則です。

そうなんだ。すごい先生だ。
いや〜ここまで突っ込んで理解されているのはすごいです。
デバッグもかなり深いところまで行ってる感じですね。
また疑問が出たら、いつでもどうぞ!
おだてるところもすごい先生ね(見習わないと)。


あれ?順番が最後にまとめてというだけで、長さは必ず渡してくれているようだよ。 最後にまとめて渡してくれるんだ。

calling_c.f

/Users/mk/.tex-inputs/knowhow-2024-fig/calling_c.f
cfunc.c

/Users/mk/.tex-inputs/knowhow-2024-fig/cfunc.c

引数として、文字列と、整数と、文字列を並べる。 文字列1へのポインター、整数へのポインター、文字列2へのポインター、 文字列1の長さ、文字列2の長さ、の順に渡される。

% gfortran calling_c.f cfunc.c
% ./a.out
s=0x7ff7b70483d2, *s=HELLO, length(*s)=5, n=0x7ff7b70483dc, *n=3, s2=0x7ff7b70483cd, *s2=bye, length(*s2)=3, len_s=10, len_s2=5
s=0x108ebae86, *s=world, length(*s)=5, n=0x108ebaef0, *n=4, s2=0x108ebae80, *s2=happy, length(*s2)=5, len_s=6, len_s2=6
 %

この実行結果を見ると、色々分かる。(昔、 まだ Fortran プログラムを読んだり書いたりしていた頃、 調べたのだけれど文字列周りは良く分からず、放置したままで来たけれど、 今回はまあまあの理解ができた。)


(なぜ ChatGPT は間違えたのか。誰か勘違いしている人が書いたものを読んだからかしら。 正しいかどうか、ちゃんとチェックしないとダメなんだけど、 そういうのは出来ないのかな。)



桂田 祐史