|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
! o+ x: ^. Z- ~$ `$ K% O通过把耗时长的函数用c语言实现,并编译成mex函数可以加快执行速度。
1 A5 k" z: q/ i5 r
a* H) ?2 U. _! f0 Z \Matlab本身是不带c语言的编译器的,所以要求你的机器上已经安装有VC,BC或Watcom C中的一种。
/ H2 ^/ v' H+ |$ f9 Y& b' O/ |$ U# g: V& G" j
如果你在安装Matlab时已经设置过编译器,那么现在你应该就可以使用mex命令来编译c语言的程序了。* o, x1 F( U4 G' I
, F! Q9 S Z8 S6 N9 `* V v! O如果当时没有选,就在Matlab里键入mex -setup,下面只要根据提示一步步设置就可以了。! O9 u# v( }$ b
$ G! y Y! h' ]. K- ?" {4 G# d9 x
; S5 t( i4 k' [+ J: N4 V
; I' M, s8 f Q$ x3 ~0 c: K0 a
为了测试你的路径设置正确与否,把下面的程序存为hello.c。
' h0 V- o, T; O3 v" |; [
& q8 D, S: W/ o# W* C?0 T0 p/ }; x( C9 i0 C2 j3 i
/*hello.c*/
7 x) l3 C# Q8 d#include "mex.h"( F8 N9 N( K$ x: L
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
1 i/ B2 q3 n2 s" U; |+ ]{ mexPrintf("hello,world!/n");
# G! y' n" C9 t0 A6 t1 m' }" R}
' l/ B4 Z7 \* i: H) v1 O+ W 假设你把hello.c放在了C:/TEST/下,在Matlab里用CD C:/TEST/ 将当前目录改为C:/ TEST/(注意,仅将C:/TEST/加入搜索路径是没有用的)。现在敲:& o3 |& a# q8 z( F
mex hello.c
( l! V! W5 Z" `% w3 z# B7 O如果一切顺利,编译应该在出现编译器提示信息后正常退出。如果你已将C:/TEST/加入了搜索路径,现在键入hello,程序会在屏幕上打出一行:
' r/ p- g1 o# q, \+ q5 H; g0 ~hello,world!
6 v& ~ h* B$ k1 a, t" u' b' o* ]& u% o9 u+ g
整个程序由一个接口子过程 mexFunction构成。
4 O1 G. @, i% y8 Cvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) ' D- Y% P6 C) {, _5 S$ g- J5 b2 g
前面提到过,Matlab的mex函数有一定的接口规范,就是指这
$ W" f! O" ^- B$ Tnlhs:输出参数数目
+ Y. x- p5 y! j% h' @" c6 F% `& U) ]plhs:指向输出参数的指针
8 w7 \( p0 J9 Znrhs:输入参数数目
8 O' a& o4 X; M8 k) q7 K4 e+ A例如,使用) h0 D; O$ }9 X. e) P
[a,b]=test(c,d,e)7 B( @5 a: s$ O3 ]8 e* G! r0 p; `) S
调用mex函数test时,传给test的这四个参数分别是0 a( C, s# u* ]$ d
2,plhs,3,prhs
" j& D" e; o* _0 P7 `* P其中:
/ Z/ J% D! r: \# e2 ~ eprhs[0]=c
# ~( }( m( y% O/ j% v- l) Nprhs[1]=d
( `6 g0 k/ J2 rprhs[2]=e
# b) B6 E1 M8 s$ c; @: _7 N! l& z当函数返回时,将会把你放在plhs[0],plhs[1]里的地址赋给a和b,达到返回数据的目的。
) Q8 T+ _- e6 L6 J细心的你也许已经注意到,prhs和plhs都是指向类型mxArray类型数据的指针。 这个类型是在mex.h中定义的,事实上,在Matlab里大多数数据都是以这种类型存在。当然还有其他的数据类型,可以参考Apiguide.pdf里的介绍。
8 g" v' K" o# O8 T6 K' w. `1 S' a1 r z% R
2 A! y2 ]6 d% a/ f) r" L8 I& u+ C0 }$ b( T
//hello.c 2.0
% S" D& k, K5 [( H#include "mex.h" $ `; J8 V \* m: P: C; w2 s
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
. m7 P5 c4 B( R) e/ R{
: z2 C1 [ F9 Rint i; 7 _" r. l/ k% |/ K( k
i=mxGetScalar(prhs[0]); $ ?4 g W3 D- [9 t' g ?$ L
if(i==1) ' `: b0 k8 I( @& I
mexPrintf("hello,world!/n");
( p1 Y J% q( y. D1 ?" G1 a8 }; belse & ~2 l1 n8 ^# t- s( Z
mexPrintf("大家好!/n"); ) O( F" J, Q( _
} M- z% y; d1 K, X# g
5 R. S, Q" [7 S
( u2 ]& _& ~. g5 S将这个程序编译通过后,执行hello(1),屏幕上会打出: hello,world! 2 Z( m, y- r5 ?1 t2 b
而hello(0)将会得到: 大家好!) `6 k. |3 u$ I3 B3 a0 S, N6 N, M
4 ^3 D+ T. D' G- f- P4 U% m
用到了一个函数:mxGetScalar,调用方式如下: & O6 ~% {0 Q) } y6 c9 _: Y
i=mxGetScalar(prhs[0]); ; q+ q. p; O) Q) f& \# K* G
"Scalar"就是标量的意思。在Matlab里数据都是以数组的形式存在的,mxGetScalar的作用就是把通过prhs[0]传递进来的mxArray类型的指针指向的数据(标量)赋给C程序里的变量。这个变量本来应该是double类型的,通过强制类型转换赋给了整形变量i。
- N6 ^( C) e" J, k+ c3 r
0 K8 [/ r6 {! [) `6 Y) @6 @" F
: t. G8 R5 N$ t' r# x# W- l2 w% B; @ K$ f/ r& H
//hello.c 2.1
" E( l, G* c) j! H% R. P5 G#include "mex.h"
# q- @ T3 S: V* Jvoid mexFunction(int nlhs, mxArray *plhs[], : s6 I$ Y+ {0 c% z2 R4 S) l9 R
int nrhs, const mxArray *prhs[])
0 R, c) o; _: l& h7 o{ 2 e% b" l4 k* Q; i W
int *i; 3 v8 x& C8 C! X% U3 I
i=mxGetPr(prhs[0]);
G* V( ~( P$ r' dif(i[0]==1) ) |/ i: R0 f* K! b6 ^
mexPrintf("hello,world!/n");
* f3 y' w7 r( velse
" c5 B( l& O5 t3 n2 m" R mexPrintf("大家好!/n");
; e: T' J+ F1 S" }} / p0 b3 o2 \ l2 H) j/ g
$ F A! x2 }+ H/ \; c
! Y+ z9 \0 I4 r, X4 ?
* w- B6 V4 H ^$ k" H- k! ~( v8 _6 x这样,就通过mxGetPr函数从指向mxArray类型数据的prhs[0]获得了指向double类型的指针。: Q A( b" h7 m% c3 m
但是,还有个问题,如果输入的不是单个的数据,而是向量或矩阵,那该怎么处理呢 ?通过mxGetPr只能得到指向这个矩阵的指针,如果我们不知道这个矩阵的确切大小,就
- X+ ?' o4 g; ?! k h没法对它进行计算。 2 P3 a1 g, e, u' Z1 {) p( Y
为了解决这个问题,Matlab提供了两个函数mxGetM和mxGetN来获得传进来参数的行数 和列数。下面例程的功能很简单,就是获得输入的矩阵,把它在屏幕上显示出来: / {* J7 f7 I# O$ G( R
' H; v3 g1 S$ c0 B# Q( Y3 C4 s: K$ f7 P
7 Z B" C, W( n' f; G//show.c 1.0
4 l' ^. Y H5 i7 ^1 h, j P% r#include "mex.h"
: C7 F4 r9 a, Q* l- B% u, U6 v% i" N3 o% |#include "mex.h" ' X2 J% C( j8 y
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
. J2 i0 S* }4 E{
1 _& ~* P- r4 F, D, adouble *data; 2 [9 f% k) e' r0 P, V
int M,N; 2 R6 y2 @& t1 e# S- _ f
int i,j;
; U( ?; [8 v$ g( Z) m( W/ kdata=mxGetPr(prhs[0]); //获得指向矩阵的指针
8 g5 g0 H6 N5 F3 i$ U# dM=mxGetM(prhs[0]); //获得矩阵的行数 : M/ E0 q! N0 f7 Z/ F7 \
N=mxGetN(prhs[0]); //获得矩阵的列数
' p/ M& K, x( I- @. m' pfor(i=0;i<M;i++)
: s8 j) H3 C+ p( d% b% G+ z9 m{ for(j=0;j<N;j++) ; c! e9 C5 X( y! j6 V8 P
mexPrintf("%4.3f ",data[j*M+i]); , w- a5 G8 m* Z+ g& m, J" q0 ^. p
mexPrintf("/n");
; J! P# B {, Z+ \/ v }
2 P0 \' h0 h) t! ~3 ^} 5 g% j+ H! p% P
6 g) k0 p. q/ S0 Z
* n+ p) V* t. L0 Y& y* r# U! \- w
# F$ V [/ e( X& i% ?2 {编译完成后,用下面的命令测试一下:
6 z0 H! {0 D% v1 l a=1:10;
' i. g) [0 D1 X8 p% H b=[a;a+1]; . C) I' T+ `+ G2 l8 @* o
show(a) 7 P$ U4 E. e+ y( N
show(b) ; s( n) y+ W% R) F
需要注意的是,在Matlab里,矩阵第一行是从1开始的,而在C语言中,第一行的序数为零,Matlab里的矩阵元素b(i,j)在传递到C中的一维数组大data后对应于data[j*M+i] 。
: H; S$ ]/ q" d0 T% s0 W输入数据是在函数调用之前已经在Matlab里申请了内存的,由于mex函数与Matlab共用同一个地址空间,因而在prhs[]里传递指针就可以达到参数传递的目的。但是,输出参数却需要在mex函数内申请到内存空间,才能将指针放在plhs[]中传递出去。由于返回指针类型必须是mxArray,所以Matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存的申请,函数原型如下:
* i. M& g* ?8 g/ S& n! Z mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag) 8 O8 \3 e+ k. ]; w1 m8 }
m:待申请矩阵的行数
) [; v ?: r( g n:待申请矩阵的列数 & o- Z/ K" `3 _0 }# x
为矩阵申请内存后,得到的是mxArray类型的指针,就可以放在plhs[]里传递回去了。但是对这个新矩阵的处理,却要在函数内完成,这时就需要用到前面介绍的mxGetPr。使用 mxGetPr获得指向这个矩阵中数据区的指针(double类型)后,就可以对这个矩阵进行各种操作和运算了。下面的程序是在上面的show.c的基础上稍作改变得到的,功能是将输 ) ~* Y$ v p# A7 v/ I/ ]# f4 m
) O0 \! |7 d1 D ^9 P3 A
9 V( D6 C! ]. U
- L4 N6 c& `6 y- ^& i$ N b7 ^- i, F2 T7 ^! V B! W* b
//reverse.c 1.0
$ g6 B% }: t( i3 C* `+ `#include "mex.h" $ U+ q& @3 R& k) Q" R* Q' W! P
void mexFunction(int nlhs, mxArray *plhs[], : N5 [3 F# w Q4 M3 j; M* E
int nrhs, const mxArray *prhs[])
+ b0 x( C: c6 k" I( i{
* S. M1 u+ _1 ?! Q. I: G6 D, adouble *inData;
9 C% {- R* ]+ N0 D- s' L0 o. gdouble *outData; 3 O# z; X1 x7 n
int M,N;
4 |" I! N8 G7 k0 Iint i,j; - S8 A% b* W& l; [: B; L* I1 G
inData=mxGetPr(prhs[0]);
( J ?2 R: n- q8 L" S' |; _M=mxGetM(prhs[0]);
: @2 {6 c) H4 v" aN=mxGetN(prhs[0]);
3 S4 m) n3 P9 o/ Y4 p" Zplhs[0]=mxCreateDoubleMatrix(M,N,mxREAL);
# y1 b' O m: A5 AoutData=mxGetPr(plhs[0]); : I' B: M- |7 ]" y$ W- f
for(i=0;i<M;i++) . J4 r: ?: P( g
for(j=0;j<N;j++)
' S9 v4 o6 k% _; f outData[j*M+i]=inData[(N-1-j)*M+i];
5 o& e5 K2 u8 \2 r5 _4 ^}
6 N! J! S; y, w) j" M8 d3 i8 a$ E: T& I9 }2 {3 B
" }' j' f9 T9 ]7 e# q# N
2 H- b' G$ `0 k5 ~0 l* {3 m当然,Matlab里使用到的并不是只有double类型这一种矩阵,还有字符串类型、稀疏矩阵、结构类型矩阵等等,并提供了相应的处理函数。本文用到编制mex程序中最经常遇到的一些函数,其余的详细情况清参考Apiref.pdf。
+ P: j! m1 F$ E( h* H" y通过前面两部分的介绍,大家对参数的输入和输出方法应该有了基本的了解。具备了这些知识,就能够满足一般的编程需要了。但这些程序还有些小的缺陷,以前面介绍的re由于前面的例程中没有对输入、输出参数的数目及类型进行检查,导致程序的容错性很差,以下程序则容错性较好
. F9 A; K1 Q8 T4 w/ Y0 G# T- n2 _( A
e& D T( C+ \1 n
#include "mex.h"
: S' D$ y: D4 E E: P v( gvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
" [/ E# e5 j6 R1 E. v% _/ {( a{ & @- p7 U9 `. Z4 C0 F' o2 g
double *inData;
* [! r) l5 n" p3 v. ~$ z+ ddouble *outData; 7 ~* d, ?$ d2 X2 C. z& c
int M,N;
9 j. ?2 D8 @. O: v8 i//异常处理 6 M2 M8 E9 A6 S; `! T4 O
//异常处理 : t) W! Z$ P. C+ W8 ]6 v3 r
if(nrhs!=1)
0 t+ i; E' c7 j3 l mexErrMsgTxt("USAGE: b=reverse(a)/n");
1 e Q5 z0 t" B! C if(!mxIsDouble(prhs[0])) 3 T$ T1 O3 K% D9 M2 H& D
mexErrMsgTxt("the Input Matrix must be double!/n");
6 l9 S( f5 J, A* y# i) e j6 g7 x inData=mxGetPr(prhs[0]); - c% U# }4 x5 I8 N+ V
M=mxGetM(prhs[0]);
* b( P1 W* f: \' ` N=mxGetN(prhs[0]);
( j. S" Y5 [8 a: d2 p1 ^# @ plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL);
, s# j- B5 j( Q outData=mxGetPr(plhs[0]);
9 p! x$ L! u. P" ?' M1 ` for(i=0;i<M;i++)
5 I N% m9 x. `+ F& ^( @4 U( T for(j=0;j<N;j++)
* C) {1 @7 v1 E( t/ [ outData[j*M+i]=inData[(N-1-j)*M+i]; + S% Y% M4 z6 b D, u
}
, q" J+ |8 [" V# f5 L
. j/ a: R- |/ t5 D! i # Z4 i' M | o; k! l
! h2 S, F# `' X在上面的异常处理中,使用了两个新的函数:mexErrMsgTxt和mxIsDouble。MexErrMsgTxt在给出出错提示的同时退出当前程序的运行。MxIsDouble则用于判断mxArray中的数据是否double类型。当然Matlab还提供了许多用于判断其他数据类型的函数,这里不加详述。
( Q# | H% J% j& t- ^' f需要说明的是,Matlab提供的API中,函数前缀有mex-和mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mx前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mxErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。
' z( X! T% Y8 y- u, ~至此为止,使用C编写mex函数的基本过程已经介绍完了。 " {/ e1 S; X7 e9 y
, _3 x. ~8 R3 \ R1 V# b4 c |
|