分からないことを ChatGPT に尋ねて、なるほどーと思ったら、実は間違いだった (正しいことを教えてくれなかった)。 でも、自分の理解は進んだ、という話。
call writec('Hello') |
void writec(char *buf, int len) |
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 % |
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 が暗黙に長さ引数を追加するかどうかは、 引数の順番(最後かどうか)や、 他の引数の型や数(実数が続いている、明示の長さがある)などに依存し、 コンパイラの実装によって異なる可能性があります
✅ よって、実用上の原則
これが一番安全でポータブルな方針です。
🛡 まとめ
そのため、リテラルには // 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 は間違えたのか。誰か勘違いしている人が書いたものを読んだからかしら。 正しいかどうか、ちゃんとチェックしないとダメなんだけど、 そういうのは出来ないのかな。)