通过把耗时长的函数用c语言实现,并编译成mex函数可以加快执行速度。Matlab本身是不带c语言的编译器的,所以要求你的机器上已经安装有VC,BC或Watcom C中的一种。
如果你在安装Matlab时已经设置过编译器,那么现在你应该就可以使用mex命令来编译c语言的程序了。。需要注意的是,较低版本的在设置编译器路径时,只能使用路径名称的8字符形式。
目录
1 编译器MinGW-w64安装
1.1 tdm-gcc下载:
http://tdm-gcc.tdragon.net/download
1.2 tdm-gcc安装:
点击exe文件进入安装界面——>将check for updated files一项取消勾选——>create时候建议——>一路下一步,完成安装。
1.3 配置系统环境变量:
电脑——>属性——>高级系统设置——>高级——> 环境变量——>系统变量——>新建:增加变量名MW_MINGW64_LOC,变量值为TDM-GCC-64的安装路径。
2 编写不带输入接口的C
2.1 C文件格式
为了测试设置正确与否,把下面的程序存为hello.c。
/*hello.c*/
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mexPrintf("hello,world!\n");
}
2.2 m文件格式
假设把hello.c放在了C:\TEST\下,并作为matlab的工作路径,在该路径下新建.m文件helloM.m。文件内容:
%% helloM.m
mex hello.c
hello
2.3 程序运行
执行该m文件,编译应该在出现编译器提示信息后正常退出,并生成一个新文件:hello.mexw64。现在键入hello(实际执行hello.mexw64),程序会在屏幕上打出一行:
hello,world!
3 带输入参数的C
3.1 函数mexFunction接口规范
分析hello.c,可以看到程序的结构是十分简单的,整个程序由一个接口函数 mexFunction构成。
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
nlhs :lhs-left hand side,说明调用语句左手面有nlhs个变量。
nrhs :rhs-right hand side,说明调用语句右手面有nrhs个自变量,由m文件调用时输入变量个数决定。
plhs :是一个指针数组,其内容为指针,该指针指向数据类型mxArray。
若现在左手面只有一个变量(nlhs=1),即该数组只有一个指针,plhs[0]指向的结果会赋值给matlab中对应的变量。
prhs :是一个指针数组,指向m文件调用时输入的变量。
若右手面有两个(nrhs=2)自变量(b,c),即该数组有两个指针,prhs[0]指向了b,prhs[1]指向了c。
要注意prhs是const的指针数组,即不能改变其指向内容。
例如,使用
[a,b]=test(c,d,e)
调用mex函数test时,传给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里的介绍。
3.2 带输入参数的C
为了解参数传递的过程,把hello.c改写一下,使它能根据输入参数的变化给出不同的屏幕输出:
//hello.c 2.0
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
int *pin, in;
pin = mxGetPr(prhs[0]);
in = *(mxGetPr(prhs[0]));
if(in==1)
mexPrintf("hello,world!\n");
else
mexPrintf("大家好!\n");
}
3.3 运行带参数的C
将这个程序编译通过后,执行hello(1),屏幕上会打出:
hello,world!
而hello(0)将会得到:
大家好!
现在,程序hello已经可以根据输入参数来给出相应的屏幕输出。
4 矩阵作为C的输入
4.1 矩阵在matlab和C中的不同
通过mxGetPr只能得到指向这个矩阵的指针,如果我们不知道这个矩阵的确切大小,就没法对它进行计算。
为了解决这个问题,Matlab提供了两个函数mxGetM和mxGetN来获得传进来参数的行数 和列数。下面例程的功能很简单,就是获得输入的矩阵,把它在屏幕上显示出来:
//show.c 1.0
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *inData;
int M,N;
int i,j;
inData=mxGetPr(prhs[0]);
M=mxGetM(prhs[0]);
N=mxGetN(prhs[0]);
// 打印输入矩阵
printf("行:%d, 列:%d\n", M, N);
for(i=0;i<M;i++) {
for(j=0;j<N;j++)
printf("%lf ",inData[i*N+j]);
printf("\n");
}
}
m文件输入以下内容:
a=1:12;
a = reshape(a,3,4)
mex show_1.c;
show_1(a)
会输出:
a =
1 4 7 10
2 5 8 11
3 6 9 12
使用 'MinGW64 Compiler (C)' 编译。
MEX 已成功完成。
行:3, 列:4
1.000000 2.000000 3.000000 4.000000
5.000000 6.000000 7.000000 8.000000
9.000000 10.000000 11.000000 12.000000
需要注意的是:mxGetM()获得matlab数组的行,mxGetN()获得matlab数组的列。但是在传递数组时需要注意matlab中与c中数组数据的表现形式的不同。
如上面数组在内存中按顺序依次存储:1,2,3,4,...,12
Matlab的数组变现3行4列:
1 4 7 10
2 5 8 11
3 6 9 12
c中的数组格式若也要维持3行4列,依次取内存中的数据,填入数组中,则输出:
1.000000 2.000000 3.000000 4.000000
5.000000 6.000000 7.000000 8.000000
9.000000 10.000000 11.000000 12.000000
4.2 以矩阵作为输入时,nrhs的数据类型
matlab中不需要定义数据类型,但C中需要确定的数据类型,因此C中接收matlab数据时,一般默认为double类型,下面对比不同之类类型接收数据的区别:
/*hello.c 2.1*/
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
int *pin;
double *dpin;
pin=mxGetPr(prhs[0]);
dpin=mxGetPr(prhs[0]);
mexPrintf("pin[1]:%d\n",pin[1]);
mexPrintf("pin[1]:%f\n",dpin[1]);
}
%% 编译C
mex hello_2_1.c
a = [1,2,3];
b = [2,3,4];
%% 执行C
fprintf('------------\n')
hello_2_1(a)
fprintf('************\n')
hello_2_1(b)
运行m文件,输出结果:
------------
pin[1]:1072693248
pin[1]:2.000000
************
pin[1]:1073741824
pin[1]:3.000000
可见C中接收matlab的数据类型默认为double,即使matlab中传入的数据为整型。
5 带输出参数的C
输入数据是在函数调用之前已经在Matlab里申请了内存的,由于mex函数与Matlab共用同一个地址空间,因而在prhs[]里传递指针就可以达到参数传递的目的。但是,输出参数却需要在mex函数内申请到内存空间,才能将指针放在plhs[]中传递出去。由于返回指针类型必须是mxArray,所以Matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存的申请,函数原型如下:
mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag)
m:待申请矩阵的行数
n:待申请矩阵的列数
为矩阵申请内存后,得到的是mxArray类型的指针,就可以放在plhs[]里传递回去了。但是对这个新矩阵的处理,却要在函数内完成,这时就需要用到前面介绍的mxGetPr。使用 mxGetPr获得指向这个矩阵中数据区的指针(double类型)后,就可以对这个矩阵进行各种操作和运算了。
//reverse.c 1.0
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *inData;
double *outData1, *outData2;
int M,N;
int i,j;
//异常处理
if(nrhs!=1)
mexErrMsgTxt("USAGE: [b,c]=reverse(a)\n");
if(nlhs!=2)
mexErrMsgTxt("USAGE: [b,c]=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]);
// 打印输入矩阵
printf("行:%d, 列:%d\n", M, N);
for(i=0;i<M;i++) {
for(j=0;j<N;j++)
printf("%lf ",inData[i*N+j]);
printf("\n");
}
// 每一行翻转
plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL);
outData1=mxGetPr(plhs[0]);
for(i=0;i<M;i++)
for(j=0;j<N;j++)
outData1[j*M+i]=inData[(N-1-j)*M+i];
// 矩阵翻转
plhs[1] = mxCreateDoubleMatrix(N,M,mxREAL);
outData2 = mxGetPr(plhs[1]);
for(i=0;i<N;i++)
for(j=0;j<M;j++)
outData2[i*M+j]=inData[i*M+j];
}
m文件内容如下:
a = 1:12;
a = reshape(a,3,4)
mex reverse.c
[b, c] = reverse(a)
在上面的异常处理中,使用了两个新的函数:mexErrMsgTxt和mxIsDouble。MexErrMsgTxt在给出出错提示的同时退出当前程序的运行。MxIsDouble则用于判断mxArray中的数据是否double类型。当然Matlab还提供了许多用于判断其他数据类型的函数,这里不加详述。
需要说明的是,Matlab提供的API中,函数前缀有mex-和mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mx前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mxErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。