|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
4 `" t7 m% b% v通过把耗时长的函数用c语言实现,并编译成mex函数可以加快执行速度。* e' M' s. |3 g+ M
) y8 J) }8 e5 D' L9 n, x' lMatlab本身是不带c语言的编译器的,所以要求你的机器上已经安装有VC,BC或Watcom C中的一种。% [6 }% Z4 i5 i& T! Q
" w3 O" l/ J; t2 _: m如果你在安装Matlab时已经设置过编译器,那么现在你应该就可以使用mex命令来编译c语言的程序了。
) d4 b* b* ?; O1 p; h, N a% o0 p5 N3 r0 F+ E8 l5 F7 O2 g7 r
如果当时没有选,就在Matlab里键入mex -setup,下面只要根据提示一步步设置就可以了。( }& o- d- t* v( L$ R' J( H* J
6 h& ]. B' ?( u9 |- F% d7 n" s2 L1 t7 P- f7 I
为了测试你的路径设置正确与否,把下面的程序存为hello.c。
/ M% N3 {1 Q+ Y% g; u4 u+ n3 H1 w$ h4 s% s$ _
- /*hello.c*/
- #include "mex.h"
- void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
- { mexPrintf("hello,world!/n");
- }- l5 V" ]" g& ~/ A5 }
) x& K/ B6 N' ~9 U7 I
3 [; }4 w7 }% k3 W 假设你把hello.c放在了C:/TEST/下,在Matlab里用CD C:/TEST/ 将当前目录改为C:/ TEST/(注意,仅将C:/TEST/加入搜索路径是没有用的)。现在敲:
& V1 C) X1 q) omex hello.c / d: x2 `" T7 ~7 K/ k5 t9 m; e+ ]
如果一切顺利,编译应该在出现编译器提示信息后正常退出。如果你已将C:/TEST/加入了搜索路径,现在键入hello,程序会在屏幕上打出一行:2 F8 p& o$ L! S- |. I' g0 ]& {: @
hello,world! 9 U6 @% i6 z) `6 l- V3 N9 M9 \9 ?- G" ]
/ Z( v" L3 t- [ Z
整个程序由一个接口子过程 mexFunction构成。
9 ?* _, @* m$ {$ W' z& F6 w0 P4 {void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) ; C( _( i) K( e. m
前面提到过,Matlab的mex函数有一定的接口规范,就是指这
- T6 h n; U0 p0 M3 I5 t- c5 b) Tnlhs:输出参数数目
8 z; _# Y5 r: {9 J3 {3 m! P( tplhs:指向输出参数的指针 0 d7 J. s y6 _( w
nrhs:输入参数数目 9 q0 f1 I& [2 t
例如,使用
$ U5 t3 }! X6 L: _[a,b]=test(c,d,e)
8 f( U! ]* G. V, [. m6 L' n8 Q调用mex函数test时,传给test的这四个参数分别是
4 v. K7 T% N* |( ]& t) W5 D 2,plhs,3,prhs
! ], | P5 x: I其中: / u) z- D5 m+ |- r# ]/ T
prhs[0]=c # J3 a% t: d1 B$ C3 `: J0 |1 G" p
prhs[1]=d & [, D: M9 ]9 f8 y- M( |( h7 X
prhs[2]=e / q+ _0 k4 A) K0 d% O
当函数返回时,将会把你放在plhs[0],plhs[1]里的地址赋给a和b,达到返回数据的目的。 9 c6 c1 t! x: g; m
细心的你也许已经注意到,prhs和plhs都是指向类型mxArray类型数据的指针。 这个类型是在mex.h中定义的,事实上,在Matlab里大多数数据都是以这种类型存在。当然还有其他的数据类型,可以参考Apiguide.pdf里的介绍。 9 `! g9 c3 d- l" L
6 s7 w" O% D) x/ Z, s$ |8 \' S- //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");
- }
3 @ K+ l1 T! [$ R5 q
6 J) W q5 l' z9 ]
# q7 S8 q# M2 S8 S将这个程序编译通过后,执行hello(1),屏幕上会打出: hello,world! ; o" L. W' }0 z( p: [" s- [
而hello(0)将会得到: 大家好!! b% i/ t! j o: k
3 B5 |! Z: B& X5 w6 ~$ \用到了一个函数:mxGetScalar,调用方式如下: + c3 D1 Z6 d" L J. q+ \ S1 Y9 o
i=mxGetScalar(prhs[0]);
9 ]+ f' p# K9 ^: r! R+ _# Z2 q/ Z"Scalar"就是标量的意思。在Matlab里数据都是以数组的形式存在的,mxGetScalar的作用就是把通过prhs[0]传递进来的mxArray类型的指针指向的数据(标量)赋给C程序里的变量。这个变量本来应该是double类型的,通过强制类型转换赋给了整形变量i。
7 Q# k1 ~/ H4 `2 f: c& e. m
2 T5 B# q" d$ `- g9 n' z6 o/ b- //hello.c 2.1
- #include "mex.h"
- void mexFunction(int nlhs, mxArray *plhs[],
- int nrhs, const mxArray *prhs[])
- {
- int *i;
- i=mxGetPr(prhs[0]);
- if(i[0]==1)
- mexPrintf("hello,world!/n");
- else
- mexPrintf("大家好!/n");
- }
# q6 F6 {2 W. W
7 u+ w2 ]& R2 Y( o6 W8 ?! }8 u: I8 F$ ~; L# r$ _
这样,就通过mxGetPr函数从指向mxArray类型数据的prhs[0]获得了指向double类型的指针。0 _/ @0 Y. i2 Q# U4 a% k3 H
但是,还有个问题,如果输入的不是单个的数据,而是向量或矩阵,那该怎么处理呢 ?通过mxGetPr只能得到指向这个矩阵的指针,如果我们不知道这个矩阵的确切大小,就
6 L t- N) z% ~% Z& m3 z& U没法对它进行计算。 6 i2 J- p V! T+ v* a
为了解决这个问题,Matlab提供了两个函数mxGetM和mxGetN来获得传进来参数的行数 和列数。下面例程的功能很简单,就是获得输入的矩阵,把它在屏幕上显示出来:
9 v0 h! o M+ Y/ b; u5 A6 I; k$ `" w& Y" v3 q7 y
- //show.c 1.0
- #include "mex.h"
- #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<M;i++)
- { for(j=0;j<N;j++)
- mexPrintf("%4.3f ",data[j*M+i]);
- mexPrintf("/n");
- }
- }7 y! e2 w( d+ N
U f% J5 _" p/ }$ B/ d
+ d0 L; k2 B2 l i" f6 H7 W, o9 _# }编译完成后,用下面的命令测试一下: ; |6 K4 W+ R; I0 L( x5 f! j( z' P
a=1:10;
, R1 y( x! b) ], M3 @/ L b=[a;a+1];
9 E6 x! I- V) ]' G; M# a0 G show(a)
9 `* g* w5 L8 p/ X9 z show(b) 9 x9 P7 |/ ^' k e( O; y& V& R2 R
" I' u- ~. j, q- V. b" @/ P& f
需要注意的是,在Matlab里,矩阵第一行是从1开始的,而在C语言中,第一行的序数为零,Matlab里的矩阵元素b(i,j)在传递到C中的一维数组大data后对应于data[j*M+i] 。 # C) n# H( X8 E8 i( H. ~: N8 |4 M
输入数据是在函数调用之前已经在Matlab里申请了内存的,由于mex函数与Matlab共用同一个地址空间,因而在prhs[]里传递指针就可以达到参数传递的目的。但是,输出参数却需要在mex函数内申请到内存空间,才能将指针放在plhs[]中传递出去。由于返回指针类型必须是mxArray,所以Matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存的申请,函数原型如下: : X9 l: [0 ^7 x% Y( v9 O$ _2 S
mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag)
4 V0 e. ?3 `4 u) R3 A m:待申请矩阵的行数
3 D W7 S. n4 c* J5 j n:待申请矩阵的列数
$ v9 q% y- i) |- w3 d8 J$ K为矩阵申请内存后,得到的是mxArray类型的指针,就可以放在plhs[]里传递回去了。但是对这个新矩阵的处理,却要在函数内完成,这时就需要用到前面介绍的mxGetPr。使用 mxGetPr获得指向这个矩阵中数据区的指针(double类型)后,就可以对这个矩阵进行各种操作和运算了。下面的程序是在上面的show.c的基础上稍作改变得到的,功能是将输
& P4 M* {* b3 w+ ]2 ]0 X5 R5 Z
& L9 l: T6 W" q! ^1 B5 G! z
& e# ^* ~2 j; z x8 \- //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<M;i++)
- for(j=0;j<N;j++)
- outData[j*M+i]=inData[(N-1-j)*M+i];
- }
. [/ U& g3 o$ n9 y5 y- C+ c
& U" W4 o( d, n2 |0 l: q # ] M! t: m9 K) x# Q% U* e! G3 F; |
当然,Matlab里使用到的并不是只有double类型这一种矩阵,还有字符串类型、稀疏矩阵、结构类型矩阵等等,并提供了相应的处理函数。本文用到编制mex程序中最经常遇到的一些函数,其余的详细情况清参考Apiref.pdf。
; R, {$ o d7 S1 O- K. R通过前面两部分的介绍,大家对参数的输入和输出方法应该有了基本的了解。具备了这些知识,就能够满足一般的编程需要了。但这些程序还有些小的缺陷,以前面介绍的re由于前面的例程中没有对输入、输出参数的数目及类型进行检查,导致程序的容错性很差,以下程序则容错性较好
) H' j* A, ]+ f% X/ o" O2 y# ~( B1 A& f) ^% ?( ]
- #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<M;i++)
- for(j=0;j<N;j++)
- outData[j*M+i]=inData[(N-1-j)*M+i];
- }
9 L$ r. O6 }& x$ B- @: ?
6 i% N6 n9 U9 v( w, q: {6 J
2 K4 r+ d; C6 ]& H在上面的异常处理中,使用了两个新的函数:mexErrMsgTxt和mxIsDouble。MexErrMsgTxt在给出出错提示的同时退出当前程序的运行。MxIsDouble则用于判断mxArray中的数据是否double类型。当然Matlab还提供了许多用于判断其他数据类型的函数,这里不加详述。
3 G/ T" J) d& Z6 t1 f' v+ `4 y需要说明的是,Matlab提供的API中,函数前缀有mex-和mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mx前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mxErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。
" Z- y$ [' g- `* s0 o3 P5 i至此为止,使用C编写mex函数的基本过程已经介绍完了。 + }9 g1 O b R* `# r/ t; @$ |
|
|