1、混合语言编程概述参数传递协议命名约定堆栈管理定义Fortran中过程的原型在VIF(XE2011)环境下混合编程概念概念:由两种以上语言写成的源代码形成的程序的过程。混合语言编程综合了不同语言之间的优点,使得编程更加快捷。Fortran适合科学计算,Visual C+更适合于图形界面编程;两者结合能加快编程开发速度。这里主要介绍Fortran和C/C+的混合编程问题!1、混合编程的难点是解决不同语言之间的通信问题2、而程序之间的通信是通过例程之间的相互调用实现的。3、不同语言间的语法规范不同,要正确通信,必须,必须建立统一的约定,即调用约定调用约定。这里的例程是不同语言的函数、过程、子例程的总
2、称调用约定参数传递参数传递命名约定命名约定堆栈考虑堆栈考虑参数传递包括以下四个方面,参数传递顺序:参数传递顺序:Fortran和C/C+实参和虚参的对应顺序都是从左到右。参数传递方式:参数传递方式:值传递或者地址传递。参数数目是否可变参数数目是否可变:是否识别可选参数参数数据类型:参数数据类型:整型,浮点,字符参数传递方式由Attributes属性指定,两种方式:方式一、方式一、c c,stdcallstdcall选项:选项:Subroutine s1(a,b,c)!DEC$attributes C:s1Real(4)a,b,cEnd subroutineC、stdcall声明过程遵循Wind
3、ows标准参数传递方式,就是C语言参数传递模式区别是什么区别是什么呢呢方式二,方式二,referencereference,valuevalue选项:选项:Subroutine s1(a,b,c)!DEC$attributes value:a,b Real(4)a,b,cEnd subroutineReference和value选项能单独指定输入参数的传递方式Reference 指定参数为引用传递,这正是Fortran默认的传值方式Value指定参数为值传递,这正可以适应C/C+的传值方式以上讨论的均是在Fortran源程序中作改动,实际上由于C/C+含有指针这一特殊类型,就不需要再对值传递和
4、引用传递另外作声明了。Subroutine s1(a,b,c)!DEC$attributes value:a,bReal(4)a,b,cEnd subroutineExtern“C”Void _ stacall s1(float a,float b,float*c);C+可以定义参数可选的函数,对于这种情况Fortran如何处理呢?Real(4)function func(x,a,b)!dec$attributes c,varying c,varying:func!dec$attributes alias:_func:funcReal(4)x,a,bEnd function由由FortranF
5、ortran调用调用C+C+程序程序extern“C”float func(float x,float a=0,float b=0)float f;f=a*x*x+b*x*x;return f;主程序:主程序:Program a1Write(*,*)func(1.0,3.0)end结论:如果Fortran需要调用带有可选参数的C+函数,须在fortran程序中用c,Varying对例程进行声明。!dec$attributes c,varying:例程名选项C的作用,在后面堆栈管理约定时详细介绍。FortranFortranC/C+C/C+Integer(2)shortInteger(4)Int
6、,longReal(4)floatReal(8)doubleFortranFortran和和C/C+C/C+对应的数据类型对应的数据类型名称约定包括变量名称、例程名称、模块名称(fortran)等。混合编程真正“混合”的是在不同语言汇编形成的目标文件。对名称的约定也就是对对名称的约定也就是对.obj.obj文件中相应名称所文件中相应名称所作的约定!作的约定!Fortran对大小写不敏感,但C/C+对大小写确实严格区分的。编译的过程编译的过程Fortran中对名称的声明分三种情况:一、缺省不管fortran源文件中名称如何,在.obj文件 中名称一律大写。二、指定C、stdcall选项 不管fo
7、rtran源文件中名称如何,在.obj文件 中名称一律小写。三、Alias指定别名在.obj文件中名称与Alias规定的名称完全相同。C/C+对名称说明:不论是缺省还是用cdecl、stdcall声明,名称均保留混合大小写。C+的命名约定和C略有不同,C+生成的obj文件中名称有对外部符号的修饰,一般在声明时加上extern“C”,使其丢掉修饰而与C的命名规则一致。语言语言属性属性.ObjObj中名称中名称namename的大小写的大小写Fortran!DEC$atrributes C_name全部小写Fortran!DEC$Attributes Stdcall_namen全部小写Fortra
8、n缺省_namen全部大写CCdel(缺省)_name大小写混合C_stdcall_namen大小写混合C+缺省_namederoction大小写混合注:注:namename为定义的函数或子例程名称为定义的函数或子例程名称 n n代表调用时堆栈地址,代表调用时堆栈地址,n n为虚参列表占堆栈的大小为虚参列表占堆栈的大小注意,注意,AliasAlias服从这些法则服从这些法则实际上,当用实际上,当用FortranFortran调用调用C/CC/C+编写的程序(含有混合编写的程序(含有混合大小写时),必须用大小写时),必须用AliasAlias声声明别名。因为明别名。因为AttributesAtt
9、ributes属属性其他选项无法实现混合大性其他选项无法实现混合大小写的声明。小写的声明。Subroutine add(a,b,c)!DEC$Attributes C:addInteger(4)a,b,cC=a+bEnd subroutineextern“C”void add(int*a,int*b,int*c);void main()int a=1,b=2,c=0;add(&a,&b,&c);printf(“c=%d”,c);Subroutine add(a,b,c)!DEC$Attributes stdcall,Alias _add12:add!dec$Attributes referen
10、ce:a,b,cInteger(4)a,b,cC=a+bEnd subroutineSubroutine add(a,b,c)!DEC$Attributes stdcall,Alias:_add12:add!dec$Attributes reference:a,bInteger(4)a,b,cC=a+bEnd subroutineSubroutine add(a,b,c)Integer(4)a,b,cC=a+bEnd subroutineextern“C”_cdecl ADD(int*a,int*b,int*c)void main()int a=1,b=2,c=0;ADD(&a,&b,&c);
11、printf(“c=%d”,c);Subroutine add(a,b,c)!DEC$Attributes c,Alias:_add12:AddInteger(4)a,b,cC=a+bEnd subroutineextern“C”_cdecl Add(int*a,int*b,int*c)void main()int a=1,b=2,c=0;Add(&a,&b,&c);printf(“c=%d”,c);Fortran调用C/C+定义的函数:programprogram mainimplicit noneimplicit nonereal(8)real(8):a=2.0,b=3.0,cinterf
12、aceinterface functionfunction add2(m,n)!dec$attributes!dec$attributes stdcall,alias:_Add216:add2stdcall,alias:_Add216:add2 real(8)real(8)m,n,add2 end functionend function end interfaceend interface printprint *,add2(a,b)readread (*,*)cendend#includeextern C double _stdcall Add2(double m,double n)ret
13、urn m+n;别名别名fortran中模块的名称Fortran模块名称在.obj文件中的形式为_MODULENAME_mp_ENTITYstacksize_MODULENAME_mp_ENTITYstacksizemodulemodule tt1implicit noneimplicit nonecontainscontainsfunctionfunction mult(a,b)!dec$attributes c:mult!dec$attributes c:mult!dec$attributes!dec$attributes value:a,bvalue:a,breal(4)real(4)a
14、,b,multmult=a*bend functionend function multend moduleend moduleextern Cvoid _cdecl add(int *a,int *b,int*c);extern Cfloat _cdecl TT1_mp_mult(float a,float b);int main()int a=1,b=2,c=0;add(&a,&b,&c);printf(c=%d,c);float ct;ct=TT1_mp_mult(a,b);printf(nct=%f,ct);scanf(%d,&c);程序之间例程的调用都是通过堆栈实现的,先将例程地址、
15、例程参数压入堆栈,然后再弹出例程参数和例程地址。堆栈满足先进后出的原则。在声明调用约定C的情况下,当执行程序由被调例程返回时,由调用程序负责出栈、清理堆栈的工作;缺省约定和Stdcall约定条件下,由被调用例程控制堆栈。正因为在C约定下,由调用程序控制堆栈,调用程序知道有多少参数被传递,占多少个字节,驻留在堆栈的什么位置等信息,所以在C约定下可以传递可变数量的参数。在C约定之外规定外部例程拥有Varying属性,就能实现例程参数数目可变。real functiion ff(x,a,b,c)!dec$attributes C,Varying:ffendfunctionextern“C”doubl
16、e(double x,double a,double b=0.0,double c=0.0)当要用Fortran调用其他语言的程序时,应该在Fortran源代码中定义例程的原型(接口块)interface 例程语句 例程 attributes 属性 参数Attributes属性 正式参数声明end 例程end interfaceAttributes Attributes 属性和前面的属性和前面的声明方法、作用完全一样声明方法、作用完全一样示例示例VS2010环境下不能同时将多个语言的源程序放在同一个项目中编译,Fortran和C/C+源程序需先编译成目标文件.obj,然后在主程序所在工程中链接
17、形成.exe文件。Fortran 调用C+C+调用Fortranfortran调用C/C+函数很容易实现,在Visual studio环境下,需要将编译器默认的运行库设置 debug multithreaded修改为multithreaded,否则会出现如下的错误:C/C+调用Fortran例程:仅仅将Fortran生成的.obj文件添加到项目中还不够,另外还需要将Fortran编译器下的一些静态库包含进来。在安装目录下:ComposerXE-2011compilerlibia32Thank you语言语言有返回值调用有返回值调用无返回值调用无返回值调用Fortran函数(Function)子例程(Subroutine)C/C+有返回值的函数空函数(void)C/C+:除数组外的其他所有非指针参数均采用值传递的方式。Fortran:除常量、表达式外所有数值型参数均采用引用传递(地址传递)。源代码编译预处理编译汇编链接可执行文件处理#define,#ifdef,#include等语句,对源代码的改写和扩充词法、语法分析,将代码翻译成中间代码将翻译的代码翻译成目标机器码目标文件彼此链接形成.exe文件编译编译.obj.obj文文件件