1. 程式人生 > >Fortran與C的混編

Fortran與C的混編

動態鏈接 調用程序 運行 vc++ clu 出現一次 可執行 裏的 lock

\(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的混編