C与Fortran之间的相互调用

本文详细介绍了如何在C++中调用Fortran编写的动态链接库(DLL)以及Fortran调用C动态链接库的过程。包括创建FORTRAN DLL、C++调用DLL的步骤、Fortran静态和动态调用C函数的方法,并提供了相应的源代码示例。
摘要由CSDN通过智能技术生成

此文章鸣谢

@Dragon  @竹叶知风飘 排名不分先后

运行环境

C/C++编译器:MSVS2019(VC9),对VS2008也适用。
Fortran编译器:Intel Visual Fortran Compiler 11.0,IVF的其它版本也适用。
 

C调用Fortran函数


一、创建FORTRAN DLL工程,生成forsubs.dll文件供调用。
 用IVF建立一个Dynamic-link library项目,比如项目名为forsubs.dll。在项目中新建f90代码文件,比如文件名为forsubs.f90。在forsubs.f90中写下如下代码:

用IVF建立一个Dynamic-link library项目,比如项目名为forsubs.dll。在项目中新建f90代码文件,比如文件名为forsubs.f90。在forsubs.f90中写下如下代码:

! forsubs.f90
!
! FUNCTIONS/SUBROUTINES exported from FORSUBS.dll:
! FORSUBS - subroutine
!
INTEGER*4 FUNCTION Fact (n)
!DEC$ ATTRIBUTES DLLEXPORT::Fact
INTEGER*4 n [VALUE]
INTEGER*4 i, amt
amt = 1
DO i = 1, n
amt = amt * i
END DO
Fact = amt
write(*,*)"Mixed calls succeed!"
END
SUBROUTINE Pythagoras (a, b, c)
!DEC$ ATTRIBUTES DLLEXPORT::Pythagoras
REAL*4 a [VALUE]
REAL*4 b [VALUE]
REAL*4 c [REFERENCE]
c = SQRT (a * a + b * b)
END


编译后,你会找到FortranDLL.lib 和FortranDLL.dll,它们是后面调用所需要的。因
为建立的是动态库项目,所以不会有.exe 生成。二、创建win32 console application,调用forsubs.dll。
 用MSVS2010建立一个C++的Win32控制台应用程序,当然MFC的也可以。代码如下:

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <windows.h>
using namespace std;
int main()

{
//声明调用约定
typedef int (__cdecl * FACT)(int n);
typedef void (__cdecl * PYTHAGORAS)(float a, float b, float *c);
//加载动态库文件
HINSTANCE hLibrary=LoadLibrary(L"forsubs.dll");
if(hLibrary==NULL)
{
cout<<"can't find the dll file"<<endl;
return -1;
}
//获得Fortran导出函数FACT的地址
FACT fact=(FACT)GetProcAddress(hLibrary,"FACT");
if(fact==NULL)
{
cout<<"can't find the function file."<<endl;
return -2;
}
//获得Fortran导出函数PYTHAGORAS的地址
PYTHAGORAS pythagoras=(PYTHAGORAS)GetProcAddress(hLibrary,"PYTHAGORAS");
if(pythagoras==NULL)
{
cout<<"can't find the function file."<<endl;
return -2;
}
float c;
printf("Factorial of 7 is: %d\n", fact(7));
pythagoras (30, 40, &c);
printf("Hypotenuse if sides 30, 40 is: %f\n", c);
FreeLibrary(hLibrary); //卸载动态库文件
return 0;
}


 将之前生成的forsubs.lib连同路径一起添加在:项目->属性->属性配置->连接器->输入:附加依赖项里。如果它在当前目录下,就填forsubs.lib就可以了。
 将之前生成的forsubs.dll放在当前目录下,在执行时程序要连接它的。当然,也可以把它放在任意目录下,然后通过系统的“环境变量”中的Path来指定这个目录。
编译生成,运行C++即可

Fortran调用C动态链接库(静态调用及动态调用)

1、静态调用部分(需要lib,dll)

首先先说明一下题目把,为什么Fortran调用的是C动态库而不是C++?

C语言函数库的调用和C++函数库的调用是不一样的,其不同主要是由于C++相较于C的特性造成的。

C语言中一个函数名只能代表一个函数,C++中由于函数重载的存在一个函数名并不能唯一确定一个函数。

因此在调用函数库时也就注定着C和C++必定有所区别。

而Fortran是基于C标准的,所以只能调用C的动态库。(如果必须用C++编译器编译的话,可以加入extern "C",确保特定函数使用C规则编译)

编译C动态链接库
1、首先在VS中新建动态链接库项目

2、添加.h,.cpp文件内容如下

xxxx.h文件

#include <stdio.h>

extern "C" __declspec(dllexport) void _cdecl write2file(char filename[],int a[],int n,int* val);

xxxx.cpp文件

#include "pch.h"
#include "xxxx.h"


void _cdecl write2file(char filename[], int a[], int n, int* val)
{
	
	int i;
	FILE* p = fopen(filename, "w");
	*val = 0;
	for (i=0;i<n;i++)
	{
		fprintf(p, "%d\n", a[i]);
		*val += a[i];
		a[i] = -a[i];

	}
	fclose(p);
}

这里要注意一下几点

1、extern "C"代表着以C语言的形式导出;

2、 __declspec(dllexport) 表示从DLL导出函数xxxx,与之相对应的是_declspec(dllimport)表示从dll导入到程序exe

3、堆栈管理规则

_stdcall:参数由右向左压入堆栈,堆栈由函数自己清理。

_cdecl:参数由右向左压入堆栈,堆栈由调用方清理。

msvc都是默认_stdcall的堆栈管理规则,Fortran是_cdecl,

因此在输出函数库时应显式设置它。

4、对于编译生成的.lib和.dll的作用这里做一下简单的介绍:

.lib是用来编译使用的,用来说明库里有什么东西去哪里找

.dll就是用来实现内容的。

在编译过程中,只需要知道去哪里找就够了,等到程序运行的时候才需要dll出来干活

因此,如果不想额外添加路径的话,只需要将.lib复制到项目根目录,.dll复制到可执行文件的同级目录之下就可以了。

5、Fortran调用C动态链接库
在项目->属性->链接器->输入->附加依赖项 中加入需要调用的lib文件的路径(这种办法把代码发给别人,会很麻烦的去配置文件)。

你也可以把需要的lib文件直接加入到Source Files文件当中,方法:Source Files右键添加->添加->现有项(推荐这一种方法)。

以下为Fortran代码

program f_all_c
use,intrinsic::iso_c_binding
implicit none
character(80) str
integer, parameter:: n=3
integer a(n), s

!函数接口
interface
subroutine write2file(filename,a,n,s) bind(c)
character filename(*)
integer,value:: n
integer a(n), s
end subroutine
end interface


a = [1,2,3]
str = "1.txt"// C_NULL_CHAR
call write2file(str,a,n,s)
write(*,*)s
write(*,*)a
end program

2、动态调用部分(仅需要dll)

1、创建Fortran的载入dll的模块

module LoadDllFile
    use,intrinsic::ISO_C_BINDING
    use kernel32
    implicit none
    type(c_funptr) ::fun_handle
    integer(HANDLE)::dll_handle
    contains
    subroutine loadDll(dllName,  dll_handle,stat)
        implicit none
        character(*), intent(in)     :: dllName
        integer(HANDLE), intent(out) :: dll_handle
        logical, intent(out)::stat
        stat = .false.
        dll_handle = LoadLibrary(trim(dllName)//C_NULL_CHAR)
        if(dll_handle/=NULL) stat = .true.
    end subroutine loadDll
   subroutine loadFunction(dll_handle, funName, fun_handle, stat)
        implicit none
        integer(HANDLE), intent(in) :: dll_handle
        integer(C_INTPTR_T)         :: f_handle
        type(c_funptr)              :: fun_handle
        character(*),intent(in)     ::funName
        logical, intent(out)        ::stat
        stat = .false.
        f_handle = GetProcAddress(hModule = dll_handle, lpProcName=trim(funName)//C_NULL_CHAR)
        if(f_handle /= NULL) then
            stat = .true.
            fun_handle = TRANSFER(f_handle, C_NULL_FUNPTR)
        end if
   end subroutine loadFunction
end module LoadDllFile


2、创建Fortran主函数

program f_Call_c
use,intrinsic::iso_c_binding
use LoadDllFile
implicit none
character(80) str
integer, parameter:: n=3
integer a(n), s
 
!函数接口
interface
subroutine write2file(filename,a,n,s) bind(c)
character filename(*)
integer,value:: n
integer a(n), s
end subroutine
end interface

!DLL调用
procedure(write2file), pointer::p
logical::stat

call loadDll("task_0510", dll_handle, stat)
    if(stat==.true.) then
        call loadFunction(dll_handle, "write2file", fun_handle, stat)
    else 
        print *, "NO_Dll"
    end if
 
call c_f_procpointer(fun_handle, p)

 
a = [1,2,3]
str = "1.txt"// C_NULL_CHAR
call p(str,a,n,s)
write(*,*)s
write(*,*)a
end program

3、创建C动态库

xxxx.h

#include <stdio.h>

extern "C" __declspec(dllexport) void _cdecl write2file(char filename[],int a[],int n,int* val);

xxxx.app

#include "pch.h"
#include "xxxx.h"


void _cdecl write2file(char filename[], int a[], int n, int* val)
{
	
	int i;
	FILE* p = fopen(filename, "w");
	*val = 0;
	for (i=0;i<n;i++)
	{
		fprintf(p, "%d\n", a[i]);
		*val += a[i];
		a[i] = -a[i];

	}
	fclose(p);
}

4、把生成的C的dll拷贝Fortran程序根目录运行即可

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值