1. 導入:なぜCOMMONブロックの制御が重要なのか
数値計算の現場では、数十年前のFortran 77コードを現代のシステムに組み込むことが頻繁にあります。特に「COMMONブロック」は、異なるサブルーチン間で共通のメモリ領域を共有するレガシーな手法ですが、現代のC言語やC++、最新のFortranコンパイラとリンクさせる際、最も頻繁に「リンクエラー(Undefined reference)」を引き起こす要因となります。その原因の多くは、コンパイラが自動的に付与する「名前マングリング(シンボル名の変換)」の不一致にあります。本記事では、この低レイヤの不整合を正しく制御する方法を解説します。
2. 基礎知識:名前マングリングとCOMMONブロックの仕組み
COMMONブロックとは、特定のメモリ領域に名前をつけ、複数のスコープから参照可能にする仕組みです。コンパイラは、この名前をオブジェクトファイル内に保存する際、OSやコンパイラの仕様に従って名前を加工(マングリング)します。
一般的なルールとして、多くのFortranコンパイラはシンボルの末尾にアンダースコア(_)を付与しますが、環境やコンパイラオプションによっては、末尾にアンダースコアを付けない、あるいは大文字・小文字を変換するといった挙動を示します。C言語側からこの領域にアクセスするには、コンパイラが生成するシンボル名と完全に一致させる必要があるため、この挙動の理解が不可欠です。
3. 実装/解決策:確実なリンクのための設計
この問題を解決する最も堅牢な方法は、リンカが生成するシンボル名をコンパイラが明示的に制御できるようにすることです。現代のFortran(Fortran 2003以降)では、BIND(C)属性を使用することで、名前マングリングを無効化し、C言語側の構造体とメモリレイアウトを厳密に一致させることが可能です。レガシーなCOMMONブロックを使用する場合でも、一度BIND(C)を用いたモジュール経由でアクセスするようにラッパーを書くことで、移植性を劇的に向上させることができます。
4. サンプルプログラム:C言語とFortranの安全な連携
以下は、Fortran側のCOMMONブロックをC言語から安全に参照するための構成例です。
/ C言語側のヘッダ定義 (my_common.h) /
/ Fortran側とメモリ配置を完全に一致させるため、パディングに注意が必要です /
struct MyData {
int n;
double val[10];
};
/ Fortran側 (my_common.f90) /
module common_wrapper
use, intrinsic :: iso_c_binding
implicit none
! BIND(C)を指定することで、名前マングリングを抑制し、C言語から参照可能にする
! COMMONブロックの代わりに構造体を利用するのが現代的な推奨手法
type, bind(c) :: MyData_C
integer(c_int) :: n
real(c_double) :: val(10)
end type MyData_C
! 共有メモリ領域の定義
common /myblk/ n, val
! 実際にはBIND(C)構造体を使った共有が推奨される
end module common_wrapper
5. 応用・注意点:現場で陥りやすいバグの回避策
現場で最も多いミスは、「構造体のパディング」の不一致です。C言語の構造体とFortranのCOMMONブロックでは、アライメントの計算規則が異なる場合があります。特に`double`型などの8バイト境界を意識しなければならないデータが含まれる場合、コンパイラオプション(`-fpack-struct`等)の不一致でメモリ位置がズレ、プログラムが異常終了する可能性があります。
また、大規模システムでは、古いMakefileが残っており、一部のオブジェクトだけが古いコンパイラでビルドされているケースも散見されます。リンクエラーが発生した際は、`nm`コマンド(Linux環境)を使用して、オブジェクトファイル内のシンボル名を確認することをお勧めします。
例:`nm my_object.o | grep myblk`
これにより、実際にどのようなアンダースコアが付与されているかを直接確認でき、デバッグの強力な手がかりとなります。

コメント