首页 > 代码库 > Fortran与C的混编

Fortran与C的混编

\(Fortran\) 作为用于科学计算的一种编译型语言积累了大量数值计算的库,但对于现代编程来说, \(Fortran\) 无 \(GUI\)库 是其一大短板。本文就\(Fortran\) 与 \(C\)混合编程进行简单介绍。

\(Fortran和C\) 混编共有3种方式:

  • 基于源代码的混编
  • 基于动态连接库DLL的混编
  • 基于可执行文件的混编

\(Fortran\) 和 \(C\) 语言同属于编译型语言,因此可以使用任意一种语言来编写主程序或调用程序,对另一种语言编写的例程进行调用。

例程的作用类似于函数,是某个系统对外提供的功能接口或服务的集合,例如操作系统的API服务。


基于源代码的混编

基于源代码的混编是指将 \(C\) 语言文件和 \(Fortran\) 文件放在同一个工程里面直接进行编译链接,生成可执行文件。但是随着\(VC++\)的不断升级,由于一些很重要的库文件升级到高版本 与\(Fortran\) 库文件发生冲突会导致警告和错误,所以这种方式在Windows平台上没那么顺利了。下面例子就 \(Linux\) 平台介绍 \(C\) 和 \(Fortran\) 的混编。

首先来介绍\(Fortran\) 调用 \(C\) ,下面是一段 \(C\) 函数,命名为 \(foo.c\)

 #include <stdio.h>
 void foo()
 {
  printf("foo is called!\n");
 }
 #include <stdlib.h>
 #include <stdio.h>

 extern void c_call_fortran();
 void main()
 {
    c_call_fortran();
 }
!ms$attributes c::c_call_fortran
subroutine c_call_fortran
real*8 x [value]
real*8 y [value]
real*9 z [reference]

write(6,*) 'c_call_fortran is called'
return 
end

基于动态连接库DLL的混编

基于动态链接库的混编是指将 \(Fortran\) 或者 \(C\) 语言程序做成动态链接库的形式,供另外一种语言中的主程序调用。

动态连接是把一些经常共用的程序片段做成 \(DLL\) 形式,当执行程序运行时需要调用 \(DLL\) 内的函数时, \(Windows\)系统才将 \(DLL\) 链入内存,然后 \(Windows\) 才在该\(DLL\)中寻找被调用函数,并把它的地址传给调用程序。此时不管程序中有多少的程序调用该 \(DLL\) ,内存中只有一个 \(DLL\) 的副本。

由于 \(Fortran\) 和 \(C\) 在

  • 堆栈管理
  • 目标例程命名
  • 参数传递

所遵循的规则不同,所以要使混合编译获得成功,必须全面一致地协调二者所使用的调用规定。下表是\(Fortran\) 和 \(C\) 所使用的调用约定:

\(Fortran\) \(C/C++\)
缺省约定 \(\underline{} cdecl\)
\(C\) \(\underline{} stdcall\)
\(STDCALL\)

堆栈管理

\(Fortran\) 和 \(C/C++\) 间的例程调用,其参数是通过堆栈来传递的。进栈时,例程参数从左到右依次进入,出栈时例程参数从右至左, 在清理使用完的堆栈时,是由调用程序清理堆栈还是由被调函数清理堆栈,不同的调用约定有不同的规定

\(Fortran\) \(C/C++\)
主调例程负责清理堆栈 \(C\) \(\underline{} cdecl\)
被调例程负责清理堆栈 缺省约定、\(STDCALL\) \(\underline{} stdcall\)

由于调用约定\(\;C和\; \underline{} cdecl\) 都是主调函数负责清理堆栈,所以在每一处调用点都要插入管理堆栈的代码,使得主调程序的代码稍大一些。因为是主调例程负责清理堆栈,所以主调例程知道有多少

  • 而在 \(Fortran\) 的缺省约定和\( STDCALL\) 约定以及 \(C/C++ 的\underline{} stdcall\) 约定都是被调函数负责清理堆栈,管理堆栈的代码驻留在被调用例程内,且只出现一次。

基于可执行文件的混编

在 \(Windows\) 下程序显式调用 \(dll\) 步骤分为三步

  • \(LoadLibrary\)
  • \(GetProcAdress\)
  • \(FreeLibrary\)

在 \(Qt\) 的\(QLibrary\)类显式调用 \(dll\) 的步骤为

  • \(load\)
  • \(resolve\)
  • \(unload\)

示例代码如下所示:

#include <QtWidgets/QApplication>
#include <QLibrary>

typedef int (*FUN)();
int main(int argc, char * argv)
{
  QApplication a(argc,argv);
  QLibrary mylib("v2.dll");
  
  if(mylib.load())
  {
    //加载动态连接库成功则开始解析动态连接库里的函数
    FUN test=(FUN)mylib.resolve("FUN");
    if(test)
    {
      //成功连接上test函数
      test(); //调用函数
    }
    else
    {
      //输出未成功连接上test的提示信息
    }
    mylib.unload(); //
  }
  else
  {
     //输出加载动态连接库不成功的信息
  }
}

Fortran与C的混编