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