c++和matlab混合编程,C语言与MATLAB接口
在参考文献的基础上。补充和完善。
Matlab与C/C混合编程接口及其应用
Matlab具有强大的数值计算和分析能力,C/C是最流行的高级编程语言。两者互补结合的混合编程在科学研究和工程实践中具有重要意义。从Matlab调用C/C代码和C/C调用M文件两个方面,深入研究了两者混合编程的原理和实现机制,给出了特定条件下混合编程的方法和步骤。实验表明,Matlab与C/C混合编程的接口和使用方法是有效和实用的。
1导言
Matlab是目前应用最广泛的数学软件,具有强大的数值计算、数据分析处理、系统分析、图形显示甚至符号运算等功能[1]。利用这个完整的数学平台,用户可以快速实现非常复杂的功能,大大提高工程分析计算的效率[2][3]。但相对于其他高级程序[3],Matlab程序是一个解释执行程序,不需要编译等预处理,运行速度较慢[4]。
C/C语言是目前最流行的高级编程语言之一[5]。可以直接操作操作系统、应用程序和硬件,C/C语言明显优于其他解释性高级语言。一些大型应用软件如Matlab是用C语言开发的。
在工程实践中,用户经常会遇到Matlab与C/C混合编程的问题,基于Matlab 6.5和VC6.0的开发环境,对Windows平台下两者的混合编程进行了深入研究,并给出了实例。
2 Matlab调用C/C
Matlab调用C/C主要有两种方式:使用MEX技术和调用C/C动态链接库。
在Matlab和C/C混合编程之前,必须正确设置Matlab的编译器应用程序mex和编译器mbuild[1]:
Matlab编译器应用程序的设置。
Matlab编译器mbuild的设置:Mbuild -setup。
2.1调用C/C的MEX文件
MEX是Matlab Executable的缩写,是一种“可以在Matlab中调用的C(或Fortran)语言衍生程序”[6]。MEX文件使用起来非常方便,它的调用方式和Matlab的内置函数一模一样。只需在Matlab的命令提示符下输入MEX文件名即可。
一个C/C MEX源程序通常由四个组件组成,前三个是必备内容,第四个根据实现的功能灵活选择:(1) #包含“MEX . h”;(2)MEX文件的入口函数,而MEX文件的导出名必须是mexFunction,MEX(3)mxArray;(4)API函数
用简单的例子说明C/C的MEX源程序的编写和调用过程:
#包含“mex.h”
void timeSTwo(双y[],双x[])
{ y[0]=2.0 * x[0];}
void mexFunction(int nlhs,mxArray * plhs[],int nrhs,const mxArray *prhs[])
{
双*x,* y;int mrows,ncols
如果(nrhs!=1)mexermsgtxt(需要一个输入。);
else if(nlhs 1)mexermsgtxt(太多输出参数);
mrows=MX getm(prhs[0]);ncols=MX getn(prhs[0]);
如果(!mxis double(prhs[0]) mxis complex(prhs[0]) !(mrows==1 ncols==1))
mexErrMsgTxt(输入必须是非复数标量double。);
plhs[0]=mxCreateDoubleMatrix(mrows,ncols,MX real);
x=mxGetPr(prhs[0]);y=mxGetPr(plhs[0]);timestwo(y,x);}
可以在matlab中编译,也可以直接在C环境中编译:
1).(在matlab中)用指令mex timestwo.c编译这个文件,然后在MATLAB的命令行下调用生成的mex文件。2).(在VC2008中)像通用C一样编译后会生成dll,这样就可以直接在Matlab中使用,或者复制改变后缀。mexw32。(因为以后版本的Matlab R2010b可能不支持调用带dll后缀的mex文件)
2.2调用C/C动态链接库(即常用的C程序dll不使用mex的接口函数)
Matlab提供了动态链接库DLL文件的接口[7]。有了这个接口,可以在Matlab中调用动态链接库导出的函数。Matlab DLL的接口支持各种语言编写的DLL文件。在调用DLL文件之前,需要准备好函数定义的头文件。对于C/C语言开发的DLL文件,可以使用源程序中对应的头文件;对于用其他语言开发的dll,要手动准备等价的C语言函数定义头文件。
在Matlab中使用动态链接库接口技术通常需要完成以下四个步骤:
(1)打开动态连接库文件;(2)为调用函数准备数据;(3)调用从动态链接库文件中导出的函数;(4)关闭动态连接库文件。
实现以上步骤,用到的Matlab函数有:loadlibrary、loadlibrary、calllib、libfunctions、lipointer、libstruct、libisloaded。下面举例说明Matlab调用C/C动态链接库的方法和步骤:
A.在VC环境下,新建项目——win32动态连接库——项目名Test1——空项目——完成;
B.创建新的C源文件-添加一个. cpp,内容为:#include a.h
_ declspec(dll export)int add(int a,int b){ return a b;}
C.新建一个-C/C头文件-add a.h,内容为:_ declspec(dll export)int add(int a,int b);然后编译生成Test1.dll动态链接库文件,将Test1.dll和A.H .复制到Matlab的工作目录下。
D.在Matlab的命令行下调用Test.dll: LoadLibrary (test1 , a . h );x=7;
y=8;calllib(Test1 , add ,x,y);Ans=15 unloadlibrary(Test1 )。
调用DLL动态链接库的方法为Matlab重用工程实践中积累的大量实用C/C代码提供了一种简便的方法。与调用MEX文件相比,该方法更加简单实用。不过这个接口支持C,但是不支持C库和函数的重载。在这种情况下,建议使用MEX-file。如果真的要用这种方法(调用C/C动态连接库),就要对C做一些改动,详见3358 www.mathworks.de/help/techdoc/MATLAB _外部/f43202.html # bq。
3/C调用Matlab
在工程实践中,C/C调用Matlab的方法主要有调用Matlab计算引擎、包含M文件转换的C/C文件、调用M文件生成的DLL文件。
3.1使用Matlab计算引擎
Matlab的引擎库为用户提供了一些接口函数。通过这些接口函数,用户可以在自己的程序中以计算引擎的形式调用Matlab文件。该方法采用客户/服务器模式,使用Matlab引擎连接Matlab和C/C,在实际应用中,C/C程序是客户端,Matlab是本地服务器。
C/C程序向Matlab计算引擎传递命令和数据信息,从Matlab计算引擎接收数据信息[2]。
Matlab提供了以下C语言计算引擎访问函数供用户使用[8]: Engopen、engClose、engGetVariable、engPutVariable、engEvalString、engOutputBuffer、engOpenSingleUse、engGetVisible、engSetVisible。
用下面的C语言写的,调用Matlab引擎计算方程x3?以2x=0根的源程序Example2.c为例,说明C/C调用Matlab计算引擎编程的原理和步骤:
# include windows . h # include stdlib . h
# include stdio . h # include engine . h
int PASCAL WinMain(HANDLE h instance,HANDLE hPrevInstance,
LPSTR lpszCmdLine,int nCmdShow)
{
发动机* epmxArray *P=NULL,* r=NULL
充电缓冲器[301];双poly[4]={ 1,0,-2,5 };
如果(!(ep=engOpen(NULL)))
{fprintf( stderr, \n无法启动MATLAB引擎\ n );返回EXIT _ FAILURE}
P=mxCreateDoubleMatrix( 1,4,MX real);mxSetClassName( P, P );
memcpy( ( char * ) mxGetPr( P),(char *) poly,4 * sizeof(double));
engPutVariable( ep,P);engOutputBuffer( ep,Buffer,300);
EngEvalString( ep, disp([多项式,poly2str(p, x ),根]),r=根(p));
Messagebox (null,buffer, example2展示MATLAB引擎的应用,MB _ OK);
eng close(EP);mxDestroyArray(P);返回EXIT _ SUCCESS
}
在Matlab下运行example2.exe: mex -f example2.c。运行结果如图1所示:
利用计算引擎调用Matlab的特点是:节省大量系统资源,应用程序整体性能较好,但无法脱离Matlab的环境运行,运行速度较慢。但对于一些特殊应用可以考虑[9](如三维图形显示)。
3.2 MCC编译器生成的cpp和hpp文件
Matlab自带的cc compiler-MCC可以把M文件转换成C/C代码。因此,它为C/C程序调用M文件提供了另一种方便的方法。以下是相应步骤的示例:
A.创建一个新的example3.m:函数y=ex maple 3(n)y=0;for I=1:n y=y I;目标
保存后,在命令窗口中输入:mcc -t -L Cpp -h example3。
在工作目录中生成了两个文件,example3.cpp和example3.hpp。
B.在VC中创建一个新的基于对话框的MFC应用程序Test2,添加一个按钮,并添加一个按钮响应函数。功能内容见步骤F。将上面生成的两个文件复制到VC项目的Test2目录下。
C.在VC中选择:项目-设置,选择属性表的链接选项,在下拉菜单中选择输入,在对象/库模块中添加libmmfile、libliblibmatlb、libliblibmx、libliblibmat、libliblibmatpm、libsgl、liblibmwsglm、liblibmwservices、lib。(使用Matlab图形库时需要添加后三种。)请注意,它们是用空格隔开的。并添加msvcrt.lib在忽略库中;
D.选择属性表C/C选项,从下拉菜单中选择General,保留预处理程序定义中的原始内容,添加MSVC、IBM PC和MSWind,用逗号分隔。从下拉菜单中选择预编译头选项,在“自动使用预补偿头”中添加stdafx.h,然后确认。
E.选择:工具-选项,在属性页选择“目录”,添加:c:\ MATLAB 6 p5p 1 p 1 \ extern \ include,c:\ MATLAB 6 p5p 1 p 1 \ extern \ include \ CPP;然后添加:c: \ matlab6p5p1p1 \ bin \ win32,c:\ MATLAB 6 p5p 1 p 1 \ extern \ lib \ win32 \ Microsoft \ msvc 60;注意用户的Matlab安装位置,并修改相应的目录。
F.将头文件添加到响应函数:# include MATLAB . HPP # include example 3 . HPP 该函数的响应代码为:
int I;mwArray n;n=10n=示例3(n);I=n . extract scalar(1);
CString字符串;str . format( example 3的返回值是:%d ,I);AfxMessageBox(str);
G.编译、连接和执行,结果如图2所示。
3.3 MCC编译器生成的DLL文件
Matlab的C编译器不仅可以将Matlab的M文件转换成C/C源代码,还可以完全脱离Matlab的运行环境生成独立的可执行DLL程序。所以在C/C程序中调用DLL就可以调用Matlab代码。下面用一个简单的例子来说明C/C调用M文件生成的DLL:
A.创建m文件example4.m:函数结果=example4 (para)
x=[1第3段];y=[1 3 1];plot( x,y);结果=para * 2;结束。然后在命令窗口中输入:
MCC-t-w libhg:example4-t link:Li b-h libmmfile . mlib libmwsglm . mlib example 4生成三个文件,example 4。dll,示例4。11b和例4。在工作目录中。
B.在VC中创建新的基于对话框的应用程序Test3,然后添加一个按钮和按钮响应函数。函数内容见步骤D,然后将生成的三个文件复制到Test2项目目录下。
C.VC编译环境的设置如3.2节步骤c和d;
D.将以下头文件添加到按钮函数文件中:#include example4。h ,函数响应代码为:
mxArray * para=mxCreateDoubleScalar(2);mxArray*结果;example 4 initialize();
result=mlfexample 4(para);CString字符串;
海峡。Format( %f ,mxGetScalar(result));AfxMessageBox(str);
E.编译、连接和执行,结果如图3所示。
mcc编译器生成的DLL动态链接库文件只需要包含在C/C编译环境中,通过调用export函数就可以实现原M文件的功能,极大地方便了用户的代码设计。
4结束语
本文从Matlab调用C/C代码和C/C调用M文件两个方面详细研究了Matlab和C/C的混合编程技术。对于Matlab调用C/C代码,给出了常用的MEX技术和调用C/C动态链接库的方法,并进行了比较。针对用户在实际中C/C调用Matlab时经常遇到的问题,通过研究给出了三种常见的方法及其特点:使用Matlab计算引擎的方法,混合编程后的可执行程序无法脱离Matlab的运行环境,运行速度很慢;使用mcc编译器将M文件转换成C/C文件的方法,虽然可以独立于Matlab运行环境,但是可以将生成的文件包含在C/C环境中,非常复杂;而M文件生成的DLL给用户提供了一个简单方便的C/C调用Matlab代码的方法。除了Matlab自带的mcc,Matcom还可以把M文件编译成C/C文件和DLL文件[2][8],不过混合编程的原理是一样的,这里就省略了。
MATLAB混合C/C编程调用C程序。
12073人阅读评论(13)收集报道
耗时的函数可以用C语言实现,编译成mex函数来加速执行。Matlab本身没有C语言的编译器,所以要求你的机器上已经安装了VC,BC或者WatcomC中的一个。如果你在安装Matlab的时候已经设置了编译器,现在应该可以用mex命令来编译C语言的程序了。如果当时没选,在Matlab中输入mex-setup。按照下面的提示一步一步设置就可以了。请注意,早期版本的在设置编译器路径时只能使用8个字符形式的路径名。比如我用的VC安装在路径C:/PROGRAMFILES/DEVSTUDIO,那么设置路径时应该写成:“C:/PROGRA~1”。在此设置之后,可以执行mex。要测试您的路径设置是否正确,请将下面的程序另存为hello.c
/*hello.c*/
#包含“mex.h”
voidmexFunction(intnlhs,mxArray*plhs[],intnrhs,constmxArray*prhs[])
{mexPrintf(你好,世界!/n’);
}
假设你把hello.c放在C:/TEST/下,在Matlab中用CDC:/TEST/把当前目录改成C:/TEST/(注意,只是把C:/TEST/加到搜索路径上是没有用的)。现在敲门:
墨西哥城
如果一切顺利,编译应该会在编译器提示消息出现后正常退出。如果您添加了C:/TEST/
输入搜索路径,现在键入hello,程序将在屏幕上键入一行:
你好,世界!
在C/TEST/目录中查找,您将找到另一个文件:HELLO.DLL。这样,第一个mex函数就完成了。分析hello.c,可以看到程序的结构非常简单,整个程序由一个接口子进程mexFunction组成。
voidmexFunction(intnlhs,mxArray*plhs[],intnrhs,constmxArray*prhs[])
如前所述,Matlab的mex函数有一定的接口规范,这意味着
Nlhs:输出参数数量
Plhs:输出参数的指针
Nrhs:输入参数数量
例如,使用
[a,b]=测试(c,d,e)
当调用mex函数测试时,传递给test的四个参数是
2,plhs,3,prhs
其中包括:
prhs[0]=c
prhs[1]=d
prhs[2]=e
当函数返回时,它会将你在plhs[0]和plhs[1]中的地址分配给A和B以返回数据。
请注意,您可能已经注意到prhs[i]和plhs[i]是指向mxArray类型数据的指针。这种类型是在mex.h中定义的,其实Matlab中的大部分数据都是以这种类型存在的。当然,还有其他数据类型。请参考Apiguide.pdf的介绍。
为了让大家更直观的了解参数传递的过程,我们重写hello.c,使其可以基于输入
输入参数的改变给出了不同的屏幕输出:
//hello.c2.0
#包含“mex.h”
voidmexFunction(intnlhs,mxArray*plhs[],intnrhs,constmxArray*prhs[])
{
inti
I=mxGetScalar(prhs[0]);
如果(i==1)
MEX printf(‘你好,世界!/n’);
其他
MexPrintf(大家好!/n’);
}
编译完这个程序后,执行hello(1),屏幕将键入:
你好,世界!
而hello(0)会得到:
大家好!
现在,程序hello可以根据输入的参数给出相应的屏幕输出。在这个程序中,除了屏幕输出函数mexPrintf(用法与C中的Printf函数几乎完全相同)之外,还使用了一个函数:mxGetScalar,调用如下:
I=mxGetScalar(prhs[0]);
“标量”是指标量。在Matlab中,数据以数组的形式存在。mxGetScalar的作用是将通过prhs[0]传递的mxArray类型的指针所指向的数据(标量)赋给C程序中的变量。这个变量应该是double类型的,它通过强制类型转换被赋给整形变量I。既然有标量,显然也应该有向量,否则矩阵无法传递。请看下面的程序:
//hello.c2.1
#包含“mex.h”
voidmexFunction(intnlhs,mxArray*plhs[],
intnrhs,constmxArray*prhs[])
{
int * I;
I=mxGetPr(prhs[0]);
if(i[0]==1)
MEX printf(‘你好,世界!/n’);
其他
MexPrintf(大家好!/n’);
}
这样通过mxGetPr函数从指向mxArray类型数据的prhs[0]中获得指向double类型的指针。
但是,还有一个问题。如果输入的不是单个数据,而是向量或矩阵,该怎么办?我们只能通过mxGetPr得到这个矩阵的指针。如果我们不知道这个矩阵的确切大小,我们会
无法计算。
为了解决这个问题,Matlab提供了mxGetM和mxGetN两个函数来获取传入参数的行数和列数。下面这个例程的功能很简单,就是获取输入矩阵并显示在屏幕上:
//show.c1.0
#包含“mex.h”
voidmexFunction(intnlhs,mxArray*plhs[],intnrhs,constmxArray*prhs[])
{
双*数据;
intM,N;
inti,j;
data=mxGetPr(prhs[0]);//获取指向矩阵的指针
m=MX getm(prhs[0]);//获取矩阵中的行数
n=MX getn(prhs[0]);//获取矩阵的列数
for(I=0;我我)
{ for(j=0;j j)
mexPrintf(%4.3f ,data[j * M I]);
MEX printf(/n );
}
}
编译后,用以下命令测试它:
a=1:10;
b=[a;a1];
显示(a)
显示(b)
需要注意的是,在Matlab中,矩阵的第一行从1开始,而在C语言中,第一行的序数是零。Matlab中的矩阵元素b(i,j)传递给c中的一维数组大数据后对应的是数据[j*M i]。
在调用函数之前,输入数据已经被应用到Matlab的内存中。因为mex函数与Matlab共享同一个地址空间,所以可以通过传递prhs[]中的指针来达到参数传递的目的。但是输出参数需要在mex函数中申请内存空间,这样指针才能放在plhs[]中并传递出去。由于返回指针类型必须是mxArray,Matlab特别提供了一个函数:mxCreateDoubleMatrix来实现内存的应用。该函数的原型如下:
mxArray * mxCreateDoubleMatrix(intm,intn,mxComplexityComplexFlag)
m:要应用的矩阵的行数
n:要应用的矩阵的列数
在为矩阵申请内存后,你得到一个mxArray类型的指针,可以放在plhs[]中并传递回来。但是,这个新矩阵的处理必须在函数内部完成,所以我们需要使用前面介绍的mxGetPr。使用mxGetPr得到指向这个矩阵中数据区的指针(double type)后,就可以对这个矩阵进行各种操作和运算了。下面的程序是在上面show.c的基础上稍加改动,功能就是输。
//reverse.c1.0
#包含“mex.h”
voidmexFunction(intnlhs,mxArray*plhs[],
intnrhs,constmxArray*prhs[])
{
double * inData
double * outData
intM,N;
inti,j;
in data=mxGetPr(prhs[0]);
m=MX getm(prhs[0]);
n=MX getn(prhs[0]);
plhs[0]=mxCreateDoubleMatrix(M,N,MX real);
out data=mxGetPr(plhs[0]);
for(I=0;我我)
for(j=0;j j)
out data[j * M I]=in data[(N-1-j)* M I];
}
当然Matlab使用的矩阵不仅仅是double,还有字符串型、稀疏型、结构型矩阵等等,并提供了相应的处理函数。在本文中,我们使用了一些在mex编程中最常遇到的函数,其余的细节请参考Apiref.pdf。
通过前两部分的介绍,我们应该对参数的输入输出方法有一个基本的了解。有了这些知识,你就可以满足一般的编程需求了。但是,这些程序仍然有一些小缺陷。前面介绍的re容错性差是因为前面的程序没有检查输入输出参数的数量和类型,而后面的程序容错性更好。
#包含“mex.h”
voidmexFunction(intnlhs,mxArray*plhs[],intnrhs,constmxArray*prhs[])
{
double * inData
double * outData
intM,N;
//异常处理
//异常处理
如果(nrhs!=1)
mexErrMsgTxt(用法:b=reverse(a)/n );
如果(!mxIsDouble(prhs[0]))
mexermsgtxt( theInputMatrixmustbedouble!/n’);
in data=mxGetPr(prhs[0]);
m=MX getm(prhs[0]);
n=MX getn(prhs[0]);
plhs[0]=mxCreateDoubleMatrix(M,N,MX real);
out data=mxGetPr(plhs[0]);
for(I=0;我我)
for(j=0;j j)
out data[j * M I]=in data[(N-1-j)* M I];
}
在上面的异常处理中,使用了两个新的函数:mexErrMsgTxt和mxIsDouble。MexErrMsgTxt给出错误提示并退出当前程序。MxIsDouble用于确定mxArray中的数据是否是Double。当然,Matlab也提供了很多判断其他数据类型的函数,这里就不赘述了。
需要注意的是,在Matlab提供的API中,有两个函数前缀:mex-和mx-。带有mx- prefix的函数大多是对mxArray数据进行操作的函数,如MxISDOUBLE、MxCreateDoubleMatrix等。而那些带有mex前缀的多为与Matlab环境交互的函数,如mexPrintf、mxErrMsgTxt等。了解这一点有助于在Apiref.pdf找到所需的功能。
至此,介绍了用C写mex函数的基本过程。
出发地:http://blog.csdn.net/jtop0/article/details/7657227
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。