mtalab第一次调用C编程之前,需运行mex操作,此操作在windows系统下是一个bat的批处理文件
>> mex -setup //在matlab 的command window下运行此命令
Please choose your compiler for building external interface (MEX) files:
Would you like mex to locate installed compilers [y]/n? y //是否调用本地安装好的c编译软件
Select a compiler:
[1] Lcc-win32 C 2.4.1 in D:\PROGRA~1\MATLAB\R2010b\sys\lcc
[2] Microsoft Visual C++ 2010 in C:\Program Files\Microsoft Visual Studio 10.0
[3] Microsoft Visual C++ 2008 SP1 in C:\Program Files\Microsoft Visual Studio 9.0
[4] Microsoft Visual C++ 6.0 in C:\Program Files\Microsoft Visual Studio
[0] None
Compiler: 1 //也可以选择2
Please verify your choices:
Compiler: Lcc-win32 C 2.4.1
Location: C:\PROGRA~1\MATLAB\R2010b\sys\lcc
Are these correct [y]/n? y //确认正确
Trying to update options file: C:\Users\XJT\AppData\Roaming\MathWorks\MATLAB\R2010b\mexopts.bat
From template: C:\PROGRA~1\MATLAB\R2010b\bin\win32\mexopts\lccopts.bat
Done . . .
**************************************************************************
Warning: The MATLAB C and Fortran API has changed to support MATLAB
variables with more than 2^32-1 elements. In the near future
you will be required to update your code to utilize the new
API. You can find more information about this at:
http://www.mathworks.com/support/solutions/en/data/1-5C27B9/?solution=1-5C27B9
Building with the -largeArrayDims option enables the new API.
**************************************************************************
>>
到此,matlab调用C的混合编程环境已经搭建好,下面举个简单例子:
#include
void mexFunction(int nlhs ,mxArray *plhs[] ,int nrhs, const mxArray *prhs[])
{
mexPrintf("Hello word!");
}
把上面程序保存为一个C文件,如 hello.c
在command window下运行
>> mex hello.c
结束之后在current folder 窗口里会有一个hello.mexw32文件。
在command window下运行
>> hello()
Hello word!
一个简单的hello word程序已经完成。
程序很简单,但和我们平常写的C程序并不一样,程序必须包含mex.h头文件。
C的入口函数为mexFunction相当于我们常写的main函数,此函数没有返回值,必须是void型。
nlhs 为函数输出的个数,plhs为函数输出值,
nrhs 为函数输入的个数,prhs为函数参数值。
举例:
如.m函数为 function [x,y]=shiftX(a,b,c)
则在写成C语言,nlhs为2,plhs为x,y,nrhs为3,prhs为a,b,c
C函数如何引用参数:
如果是数组则用mxGetPr(prhs[loc]) //loc是参数的位置,如是第一个参数则为0,如第二个则为1
如果是单个数值mxGetScalar(prhs[loc])
为了让大家能更直观地了解参数传递的过程,我们把hello.c改写一下,使它能根据输入参数的变化给出不同的屏幕输出:
//hello.c 2.0
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
int i;
i=mxGetScalar(prhs[0]);
if(i==1)
mexPrintf("hello,world!\n");
else
mexPrintf("大家好!\n");
}
将这个程序编译通过后,执行hello(1),屏幕上会打出:
hello,world!
而hello(0)将会得到:
大家好!
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
还有个问题,如果输入的不是单个的数据,而是向量或矩阵,那该怎么处理呢?通过mxGetPr只能得到指向这个矩阵的指针,如果我们不知道这个矩阵的确切大小,就没法对它进行计算。 为了解决这个问题,Matlab提供了两个函数mxGetM和mxGetN来获得传进来参数的行数和列数。下面例程的功能很简单,就是获得输入的矩阵,把它在屏幕上显示出来:
//show.c 1.0
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *data;
int M,N;
int i,j;
data=mxGetPr(prhs[0]); //获得指向矩阵的指针
M=mxGetM(prhs[0]); //获得矩阵的行数
N=mxGetN(prhs[0]); //获得矩阵的列数
for(i=0;i
{
for(j=0;j
mexPrintf("%4.3f ",data[j*M+i]);
mexPrintf("\n");
}
}
编译完成后,用下面的命令测试一下:
a=1:10;
b=[a;a+1];
show(a)
show(b)
需要注意的是,在Matlab里,矩阵第一行是从1开始的,而在C语言中,第一行的序数为零,Matlab里的矩阵元素b(i,j)在传递到C中的一维数组data后对应于data[j*M+i].
如果是多维数组并不能用Arr[i][j]的格式寻址,在参数在C里只是一维数组,一维数组按列排列,C语言是按行排列的,
如[a b c d;e f g h]C语言在内存中是按a b c d e f g h排列,而matlab是按 a e b f c g d h排列的。
如果要变成C语言可Arr[i][j]索引的格式,可用for循环转化,下面举个三维的例子:
for(i=0;i
inYg[i][j] = inY[inM*inN+j*inM+i];
inYb[i][j] = inY[2*inM*inN+j*inM+i];
}
转化前,只可以按inY[loc]索引,转换后可以按inYr[i][j]格式索引。
-------------------------------------------------------------------------------------------------------------------------------------------------------
输入数据是在函数调用之前已经在Matlab里申请了内存的,由于mex函数与Matlab共用同一个地址空间,因而在prhs[]里传递指针就可以达到参数传递的目的。但是,输出参数却需要在mex函数内申请到内存空间,才能将指针放在plhs[]中传递出去。
我这样理解,你输入的数据 实际上在matlab 中已经占有内存存储了,但是你现在要在mex 函数中将传入的数据进行传递,也就是说要复制给另外一个变量,那么另外一个变量实际是你在mex函数中新创建的,在matlab 函数中并没有分配空间,于是你要在这个变量分配一定的空间。
由于返回指针类型必须是mxArray,所以Matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存的申请,函数原型如下:
mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag)
m:待申请矩阵的行数
n:待申请矩阵的列数
为矩阵申请内存后,得到的是mxArray类型的指针,就可以放在plhs[]里传递回去了。但是对这个新矩阵的处理,却要在函数内完成,这时就需要用到前面介绍的mxGetPr。使用mxGetPr获得指向这个矩阵中数据区的指针(double类型)后,就可以对这个矩阵进行各种操作和运算了。下面的程序是在上面的show.c的基础上稍作改变得到的,功能是将输
//reverse.c 1.0
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
double *inData;
double *outData;
int M,N;
int i,j;
inData=mxGetPr(prhs[0]);
M=mxGetM(prhs[0]);
N=mxGetN(prhs[0]);
plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL);
outData=mxGetPr(plhs[0]);
for(i=0;i
for(j=0;j
outData[j*M+i]=inData[(N-1-j)*M+i];
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
当然,Matlab里使用到的并不是只有double类型这一种矩阵,还有字符串类型、稀疏矩阵、结构类型矩阵等等,并提供了相应的处理函数。本文用到编制mex程序中最经常遇到的一些函数,其余的详细情况清参考Apiref.pdf。
通过前面两部分的介绍,大家对参数的输入和输出方法应该有了基本的了解。具备了这些知识,就能够满足一般的编程需要了。但这些程序还有些小的缺陷,以前面介绍的/reverse.c 1.0由于前面的例程中没有对输入、输出参数的数目及类型进行检查,导致程序的容错性很差。
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
double *inData;
double *outData;
int M,N;
//异常处理
if(nrhs!=1)
mexErrMsgTxt("USAGE: b=reverse(a)\n");
if(!mxIsDouble(prhs[0]))
mexErrMsgTxt("the Input Matrix must be double!\n");
inData=mxGetPr(prhs[0]);
M=mxGetM(prhs[0]);
N=mxGetN(prhs[0]);
plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL);
outData=mxGetPr(plhs[0]);
for(i=0;i
}
在上面的异常处理中,使用了两个新的函数:
当然Matlab还提供了许多用于判断其他数据类型的函数,这里不加详述。
需要说明的是,Matlab提供的API中,函数前缀有mex-和mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mex前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mexErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
我们之所以使用Matlab,很重要的考虑是Matlab提供了相当丰富的矩阵运算函数和各种toolbox。在编制mex函数时,有时我们也会遇到一些操作,在Matlab下,只需要在mex函数里调用Matlab命令,我们就需要用到一个函数mexCallMATLAB,原型如下:
int mexCallMATLAB(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[],
const char *command_name);
有了前面的基础,使用这个函数就显得十分容易了。下面给出一个例程,
// set.c
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
double *inData;
mxArray *IN[1];
mxArray *OUT[1];
double *outData;
int M,N;
int i,j;
//异常处理
if(nrhs!=1)
mexErrMsgTxt("USAGE: b=rot(a)\n");
if(!mxIsDouble(prhs[0]))
mexErrMsgTxt("the Input Matrix must be double!\n");
//计算转置
if(mexCallMATLAB(1,OUT,1,prhs,"'"))
mexErrMsgTxt("Error when compute!\n");
//根据输入参数数目决定是否显示
if(nlhs==0)
mexCallMATLAB(0,IN,1,OUT,"disp");
else
plhs[0]=OUT[0];
}
使用下面代码测试程序
mex set.c 编译
set([1 2 3 4 5])
-------------------------------------------------------------------------------------------
打印函数:
mexPrintf()相当于matlab的disp函数,
mexPrintf函数里需要打印的部分必须用""而不能用''
子函数的问题:
子函数有两种方式,一种采用return值得方式返回值
另一种方式为利用参数返回值,把需要return的值直接赋给形参。
多个.c文件的编译:
多个.c文件中只能存在一个mexFunction函数
可用mex ('a.c','b.c','d.c')的格式编译