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