admin 管理员组文章数量: 1086019
2024年4月16日发(作者:vector java)
C语言环境中调用Matlab程序指南
甄梓宁
znzhen@
Matlab在计算方面功能强大、编写简单,但是要运行Matlab程序必须装有Matlab并且用户
界面也不够完善,因此除了计算的其他部分采用C等更规范完备的语言进行编写是较好的选
择。本文就对如何在C程序中调用Matlab程序作说明。
在C程序中调用Matlab程序有两大类方法。第一种是调用Matlab引擎,第二种是将m文
件打包成dll文件在C语言环境下调用。前者虽然易于实现,可以实时监控程序的运行,但
是独立性差,需要安装完整版Matlab,且每次调用都会启动进程;后者则实现
复杂,调试麻烦,但只需安装MCR(Matlab Component Runtime),耗费资源较少。关于MCR,
请见第四章的说明。至于两种方法需要在C环境中如何配置请见第五章。
而反过来若要在Matlab下调用C程序则一般使用Matlab自带的mex工具,在此不作介绍。
一、调用Matlab引擎
调用Matlab引擎可以在WIN32、MFC中使用,它的原理实际上相当于打开一个精简版
的Matlab然后往里面输命令。下面是调用Matlab中的加法程序add.m的例子。
先在Matlab的work目录下创建add.m文件并编写程序如下:
function s = add (a, b)
s = a+b;
在C程序中,首先打开精简版的Matlab:(所需头文件,引用库等见第五章)
Engine *ep = engOpen (NULL);
编译运行后,会自动打开一个命令行监控窗口,输入pwd就可以看到当前的工作目录,
于是需要先将工作目录转换至存放add.m的目录:
engEvalString (ep, ”cd ....work”);
engEvalString是往Matlab里输命令的函数,显然我们的目标是成功运行:
engEvalString (ep, ”s=add(a,b)”);
当然,目前Matlab中并没有a和b两个变量,因此需要在C中初始化这两个变量并转
换成Matlab基本变量类型mxArray,才能将它们输入到Matlab中。关于mxArray,在
第三章会有详细说明。下面是对a=1,b=2的转换过程:
1
double aval=1, bval=2;
mxArray *a=mxCreateDoubleMatrix(1,1,mxREAL);
mxArray *b=mxCreateDoubleMatrix(1,1,mxREAL);
mxSetPr(a,&aval);
mxSetPr(b,&bval);
于是可以往Matlab里输入了:(双引号内是在Matlab里的变量名称)
engPutVariable(ep,"a",a);
engPutVariable(ep,"b",b);
运行add.m:
engEvalString (ep, ”s=add(a,b)”);
编译运行后可以在监控窗口中查看s,确认是3无误。接下来就是怎样把s从Matlab读
入C中的问题了,也很方便:
mxArray *s=engGetVariable(ep,"s");
显示一下:
printf("s=%f",*mxGetPr(s));
最后不要忘记关闭Matlab:
engClose(ep);
用到的几个mxArray也需要释放,在此不述,具体请见第三章。
到此,调用Matlab引擎的全过程就结束了。不管m文件的函数形式有多复杂,都万变
不离其宗,改变的只有变量与mxArray相互转换那一部分,具体请见第三章。
另外,Matlab还提供了将监控窗口中的显示存入某buffer的函数。可以如下设置:
char buffer[1000];
engOutputBuffer(ep,buffer,1000);
这样,所有显示都会存入buffer中,这对调试很有帮助。如果想取消这个功能,则:
engOutputBuffer(ep,NULL,0);
最后还可以将监控窗口关闭或重新打开:
engSetVisible(ep,false);
engSetVisible(ep,true);
以上便是调用Matlab引擎的全部内容。这种方法步骤简单,调试也方便,所以在一般
场合下使用已经足够好了。
2
二、打包m文件至dll
打包m文件至dll相对于调用Matlab引擎,唯一也是最重要的一点好处是便于发布。
在一台没有安装Matlab的电脑中,利用最少的资源运行Matlab程序只有这种方法。
该方法根据dll的不同又可分为两类:一是打包成COM或.NET组件的dll,二是传统的
dll。前者除了在WIN32、MFC中还可在.NET中运行。本文仅介绍较为简单的打包成传
统dll的方法。
下面这个例子将自己编写的加法程序add.m及乘法程序multi.m打包至并在C
程序中调用。add.m如第一章所示,multi.m如下所示。
function s = multi( a, b )
s = a*b;
第一步,设置Matlab中使用的C语言编译器:
mbuild –setup
弹出如下提示:
Please choose your compiler for building standalone MATLAB applications:
Would you like mbuild to locate installed compilers [y]/n?
选y的话会列出Matlab默认的几个编译器及其默认路径。如果没有完全符合的话则重
来选n,就会出现更多的选择:
Select a compiler:
[1] Borland C++Builder version 6.0
[2] Borland C++Builder version 5.0
[3] Borland C/C++ (free command line tools) version 5.5
[4] Lcc C version 2.4.1
[5] Microsoft Visual C/C++ version 8.0
[6] Microsoft Visual C/C++ version 7.1
[7] Microsoft Visual C/C++ version 6.0
[0] None
Compiler:
根据个人喜好输入相应编译器序号后,就会要求确认路径。若没有,则显示如下:
The default location for xxxxxxx compilers is C:xxxxxxx, but that directory does not exist on
this machine.
Use C:xxxxxxx anyway [y]/n?
选n输入其应该在的路径即可。
3
第二步,在Matlab中将add.m及multi.m文件编译为:
mcc -W lib:calc -T link:lib add.m multi.m
第一个选项-W的格式是“dll类型:生成的dll名称”,dll类型可以是C下的“lib”,C++
下的“cpplib”,COM下的“com”以及.NET下的“dotnet”。
第二个选项-T是生成类型,可以是“link:exe”生成exe文件,以及我们所用的“link:lib”
生成dll文件。
正常运行后会生成一排文件,其中有用的是以下这几个:
:编译中被排斥的项目列表。
calc.h和:C程序编写时需要。
和:C程序执行时需要。
在中有两类被排斥的项目。一类是Excluded by M-file compilabilty
rules,这类排斥是由于m文件使用的Matlab Toolbox函数中调用了被排斥的函数导致
的。一种解决办法是将需要的Toolbox函数进行简化,尽量只使用最基本的Matlab函数。
另一类是Excluded because they ship with the MCR,这类排斥没有影响,无视即可。
第三步,在C程序中调用中的函数:
首先将calc.h、、、四个文件拷贝至C程序同一目录中,并在程序
中包含calc.h以及在链接的附加依赖项中添加。
随后,可以在calc.h文件中找到我们需要使用的几个函数:
calcInitialize():初始化程序。
calcTerminate():关闭
mlfAdd(int nargout, mxArray** s, mxArray* a, mxArray* b):调用中的加法程序。
mlfMulti(int nargout, mxArray** s, mxArray* a, mxArray* b):调用中的乘法程序。
另外,初始化的时候还可以改用calcInitializeWithHandlers设置消息处理函数,这在本章
最后详细说明。下面是调用加法程序的C程序:(所需头文件,引用库等见第五章)
//初始化
calcInitialize();
//a=1, b=2转换为mxArray格式
double aval=1, bval=2;
mxArray *a=mxCreateDoubleMatrix(1,1,mxREAL);
mxArray *b=mxCreateDoubleMatrix(1,1,mxREAL);
mxSetPr(a,&aval);
mxSetPr(b,&bval);
4
//调用中的加法程序
mxArray *s=NULL;
mlfAdd(1,&s,a,b);
//显示结果
printf("s=%f",*mxGetPr(s));
//释放mxArray
mxDestroyArray(a);
mxDestroyArray(b);
mxDestroyArray(s);
//关闭
calcTerminate();
整个调用过程跟调用Matlab引擎非常类似,在程序开头运行dll初始化程序很重要,在
编程时容易忽略掉这一步导致出错。另外,在调用加法的函数mlfAdd中,第一个参量
是输出的个数,而输出都需要取mxArray*的地址,也就是mxArray**。关于mxArray的
结构,会在第三章详细说明。
以上便是m文件打包至dll并调用的全过程。
接下来,介绍一下消息处理函数。前面已经提到,采用打包dll方法调用Matlab函数会
导致调试麻烦,因此一个好的消息处理函数是必不可少的。而前面采用的dll初始化程
序calcInitialize()是不使用消息处理函数的,下面我们将使用calcInitializeWithHandlers函
数设置消息处理函数。
calcInitializeWithHandlers函数有两个输入参量:一个是error_handler,一个是print_
handler。前者是由Matlab中error函数触发的,说明调用函数彻底失败了;后者由除
了前者以外的一切显示输出触发。这两个函数的参量列表是由mclOutputHandlerFcn定
义的,格式如下:
typedef int (*mclOutputHandlerFcn)(const char *s)
一般,print_handler可以根据需要编写,而error_handler则最好弹出对话框提示错误。
参考程序如下:(需要注意的是消息处理函数必须为静态的)
static int error_handler(const char *msg)
{
printf("Error: %sn", msg);
return 0;
}
5
相应的,dll的初始化改为:
calcInitializeWithHandlers(error_handler,NULL);
最后,讨论一下对Matlab程序的修改生效问题。当调用Matlab的C程序编写完成并生
成exe、lib或dll后,突然发现Matlab程序哪里需要修改的时候,如果还要从一开始到
最后再重新生成一遍,就实在太不人性化了,所以接下来说说修改生效的方法。
调用Matlab引擎的话,非常好办。因为相当于往精简版的Matlab中输命令行,所以只
要修改相应的m文件就立即生效。而打包dll的话,只需进行前文中的第二步,将.dll
和.ctf两个文件拷至C程序的同一目录下即可。
当然,假如Matlab函数的参量列表都要修改的话,不论哪种方法都要重新生成一遍。
三、mxArray与mat文件读写
mxArray是Matlab基本变量类型,mat是Matlab基本数据文件类型,因此放在一起介
绍。
1、 mxArray概述
mxArray可以说是万能数据类型,它可以存整型矩阵、布尔型矩阵、浮点数矩阵、复数
矩阵、字符矩阵、结构矩阵、Sparse矩阵以及Cell矩阵。但是它的结构却非常简单:首
先是矩阵的基本信息,如矩阵类型、维数、各维的长度、矩阵元素是否复数等,然后是
指向数据区的指针。数据区根据矩阵类型的不同最多可以有四块,而每块都是一个一维
的数组,也就是说不管矩阵是几维的,其数据都根据Matlab制定的映射规则存放在了
一维数组中。下面是各类型矩阵的创建、赋值与取值函数列表,具体用法请参看Matlab
帮助。
矩阵
类型
矩阵元
素类型
创建函数
(不赋值)
赋值函数 取值函数
mxCreateChar mxArrayToString
mxGetChars
mxGetString
Char
wchar_t mxCreateCharArray
MatrixFromString
mxCreateString
Double
double
mxCreateDoubleMatrix
mxCreateDoubleScaler
mxSetPr
mxSetPi
mxGetPr
mxGetPi
mxCreateLogicalArray
Logical
bool
mxCreateLogicalMatrix
mxCreateLogicalScaler
mxSetData mxGetLogicals
6
int或
double
mxCreateNumericArray
mxCreateNumericMatrix
mxSetData
mxSetImagData
mxGetData
mxGetImagData
Numeric
mxSetPr mxGetPr
mxGetPi
mxGetIr
mxGetJc
Sparse
double mxCreateSparse
mxSetPi
mxSetIr
mxSetJc
Sparse
Logical
bool
mxCreateSparse
LogicalMatrix
mxSetData
mxSetIr
mxSetJc
mxGetData
mxGetIr
mxGetJc
Cell
*mxArray
mxCreateCellArray
mxCreateCellMatrix
mxSetCell mxGetCell
mxSetField
Struct
*mxArray
mxCreateStructArray
mxCreateStructMatrix
mxSetFieldByNumber
mxAddField
mxRemoveField
mxGetField
mxGetFieldByNumber
mxArray使用示例可以参见第二章的程序,其调用了mxCreateDoubleMatrix、mxSetPr、
mxGetPr三个函数。
下面是关于使用mxArray的一些说明。因为很长很详细,所以可以跳过。
关于矩阵类型:根据矩阵元素类型可将矩阵分为两类,一类是直接存放值的矩阵,如
Double、Logical等等;另一类是如Cell、Struct等存放矩阵指针*mxArray的矩阵。对于
一个Cell矩阵,其每个元素可以是任意类型矩阵,甚至可以是Cell矩阵本身,不过这并
不会导致循环。
关于矩阵维数:在创建矩阵时可以根据需要选择以“Scalar”结尾的标量(0维)创建
函数,以“Matrix”结尾的矩阵(0~2维)创建函数,以及以“Array”结尾的数组(0~N
维)创建函数。要注意的是,在存放double型数据时,Double和Sparse类型矩阵最多
只能支持2维,大于2维则使用Numeric矩阵。同样,存放bool型数据时,SparseLogical
最多2维,大于2维则使用Logical矩阵。
关于复数数据:存放int或double型的各类矩阵中可以选择数据是否为复数。一般是在
创建函数的最后一个参量上输入“mxREAL”代表实数,“mxCOMPLEX”代表复数。
关于数据区:赋值函数其本质是改变指向数据区的指针。前面已经提到最多有四块数据
区,分别是“Data”或“Pr”代表实数数据,“ImagData”或“Pi”代表虚数数据,而主
7
要用于存放大型稀疏矩阵的Sparse或SparseLogical数据区中还有“Ir”和“Jc”两块描
述元素位置。其中Data/ImagData可以是任意类型数据,而Pr/Pi只能是double型。
关于数据存放顺序:因为是将矩阵的所有数据
用一维数组存放,所以在初始化一维数组的时
候首先需要知道其每个数据对应于矩阵中的
哪个位置。右图是Matlab对存放顺序的说明。
每一页中的行是第一维,列是第二维,而页数
是第三维,数组以图中的字母顺序排列。当然,
Matlab提供了mxCalcSingleSubscript函数可以
获得矩阵中任意位置的序号。
最后,是一些比较用的上的函数:
mxIsxxxxxxx/mxGetClassName
前者判断是否是某类型矩阵,后者返回矩阵的类型至字符串。
mxGetNumberOfElements
获取矩阵的元素总数。
mxDestroyArray
创建的mxArray用完后利用此函数释放。
2、 mat文件读写函数
Matlab提供的mat读写函数非常人性化,下面以一个具体例子说明。在这个例子中首
先将一个矩阵写入文件再读出显示。
首先写一个2x3的整型矩阵A:
int Aval[6]={1,2,3,4,5,6};
mxArray *A=mxCreateNumericMatrix(2,3,mxINT32_CLASS,mxREAL);
mxSetData(A,Aval);
以写入方式打开mat文件:
MATFile *mfp=matOpen(".","w");
如果该文件需要在Matlab6或Matlab6.5版本下打开时,参量”w”应该改为”wL”。接下来
将矩阵A写入文件:
matPutVariable(mfp,"A",A);
关闭mat文件并释放A:
matClose(mfp);
mxDestroyArray(A);
8
上面是写入mat文件的完整过程。现在我们以读取方式打开mat文件:
mfp=matOpen(".","r");
获取矩阵至B:
mxArray *B=matGetVariable(mfp,"A");
显示一下第一行第三列数据:
int subs[2]={0,2};
printf("val=%d",
((int*)mxGetData(B))[mxCalcSingleSubscript(B,2,subs)]);
最后关闭mat文件并释放B不述。
在不知道mat文件的内容的情况下,可以使用matGetNextVariable函数一个个获取文件
内的变量及相应变量名。而另外两个函数matGetVariableInfo和matGetNextVariableInfo
返回的mxArray不包括数据区,只有矩阵的基本信息。
另外,利用matDeleteVariable函数可以删除文件中的变量。
四、关于MCR
MCR主要是为没装Matlab的客户端设计的,在上面可以运行基于Matlab的程序,可以
说MCR对于Matlab就相当于.NET Framework对于Visual Studio 2005的作用。另外其体
积只有100M,安装起来非常方便。其过程分三步:
1、 安装包的生成
在Matlab中输入:
buildmcr (路径名, 文件名)
则在该路径产生一zip文件,在发布的文件中包含该zip文件即可。
2、 拷贝至客户端
在客户端安装时,将发布文件中的zip文件解压至任意目录,记其解压后的根目录
为MCRPath。
3、 修改环境变量
在系统环境变量”Path”中添加”MCRPathruntimewin32”。
五、C语言环境下的设置
首先是在C语言环境的设置中“包含文件”一项添加“Matlab或MCR根目录extern
include”,以及在“库文件”一项中添加“Matlab或MCR根目录externlibwin32
microsoft”。
9
随后再根据程序中使用的基于Matlab的函数确定需要包含的头文件以及在链接设置的
附加依赖项中添加的库文件。我们使用的函数共有四类,如下表所示:
Matlab引擎
相关函数
mxArray
相关函数
mat文件
相关函数
mex
相关函数
函数开头字母
“eng”
“mx”
“mat”
“mex”
对应头文件
engine.h
matrix.h
mat.h
mex.h
对应库文件
其中以”mex”开头的mex相关函数用于在Matlab下调用C程序,在此也一并列出,仅
供参考。
10
版权声明:本文标题:C语言环境中调用Matlab程序指南 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/p/1713225380a624778.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论