|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
1 P* E6 F! r! u# d9 |; P
通过把耗时长的函数用c语言实现,并编译成mex函数可以加快执行速度。
, x; w$ y ?. v- w% r4 R
% ~: X' o# V9 h# vMatlab本身是不带c语言的编译器的,所以要求你的机器上已经安装有VC,BC或Watcom C中的一种。/ _* t5 W: e- d- H+ K
. w: c# I) Z+ @% E; [: M d" [如果你在安装Matlab时已经设置过编译器,那么现在你应该就可以使用mex命令来编译c语言的程序了。, h# g; e5 Y" [& j
$ j. |( Y6 P- U4 b/ L6 Z9 q
如果当时没有选,就在Matlab里键入mex -setup,下面只要根据提示一步步设置就可以了。$ i0 ?! Y# v% O; g0 R
6 b1 P" e1 r# L- z/ |% w7 i
- h! u( Q5 y2 L! z& y3 V5 b# R0 E/ R7 O9 \4 B% f* e
为了测试你的路径设置正确与否,把下面的程序存为hello.c。. @+ N/ @+ o* B# G; K
4 K0 J# l* b4 x. r }" \, v$ b
?
! u8 R8 |& F& W4 n* Q8 B$ M/*hello.c*/1 P& [# ~* S4 J: L# ]
#include "mex.h"0 A3 d' M. m" |) R) N
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])! C' x) Y8 N! t; \+ T: ^: K6 t8 y
{ mexPrintf("hello,world!/n");
0 O7 O/ r; d4 y- v7 P2 d% N}% D. e) F7 A7 P g5 T
假设你把hello.c放在了C:/TEST/下,在Matlab里用CD C:/TEST/ 将当前目录改为C:/ TEST/(注意,仅将C:/TEST/加入搜索路径是没有用的)。现在敲:
( y& `: M3 y# Cmex hello.c
9 o3 S/ R$ U: {/ _如果一切顺利,编译应该在出现编译器提示信息后正常退出。如果你已将C:/TEST/加入了搜索路径,现在键入hello,程序会在屏幕上打出一行:
" @7 U- c$ S4 N! C; g* T) `2 ohello,world! & p1 _# ?) ^% O$ k- i6 l, Q
: S6 P# u7 O/ T7 S- _# T整个程序由一个接口子过程 mexFunction构成。
- ^0 a1 k3 I2 Q% ]/ ] Gvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
8 A; T% A. R( f+ P; K3 q5 K9 p4 s前面提到过,Matlab的mex函数有一定的接口规范,就是指这6 P6 P4 Q' f7 b- N7 `
nlhs:输出参数数目 9 U2 F# g/ D0 D* J, ]+ {: `7 F
plhs:指向输出参数的指针
$ O- Z' R! S3 q. s/ O' s9 `/ inrhs:输入参数数目 1 X6 h. r( G+ V$ }* }7 Z2 G
例如,使用
( ?8 R* j. V4 M3 U r[a,b]=test(c,d,e)
L$ A0 f7 h+ e. }7 o" Z调用mex函数test时,传给test的这四个参数分别是 ~. c$ c# H2 s1 _) ~
2,plhs,3,prhs
2 W2 |1 r5 N8 ~2 S2 R. o其中: # U% N7 `3 i7 f! k/ I6 O
prhs[0]=c
) U: e# V, a) d0 x7 u# r" ^prhs[1]=d
1 u5 p+ g* n& }9 i# ~# i: N! F( `prhs[2]=e 9 X1 H6 w' s' {; i' x6 K# T v: U
当函数返回时,将会把你放在plhs[0],plhs[1]里的地址赋给a和b,达到返回数据的目的。 ' w* Y/ t. j9 V$ D8 r
细心的你也许已经注意到,prhs和plhs都是指向类型mxArray类型数据的指针。 这个类型是在mex.h中定义的,事实上,在Matlab里大多数数据都是以这种类型存在。当然还有其他的数据类型,可以参考Apiguide.pdf里的介绍。 9 q7 F2 ]5 u1 k$ f' q4 g9 [
, \! x2 ]* W( u
. j: U9 d& R7 l# u7 Y- W5 \9 l
3 u' \4 Z$ `, {$ z' _//hello.c 2.0
0 ?4 I+ h- h+ ?6 z#include "mex.h"
+ }7 t3 {/ Y3 Y8 ~) _& Q" ivoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) . X/ _, u+ N1 {% Z6 s7 N- P) k
{9 o1 E X& ?, u/ `8 R R) H
int i; 8 p7 V: ~ I3 x7 \ j
i=mxGetScalar(prhs[0]);
# g$ B7 ^* |) \6 Iif(i==1)
" K( B0 C8 Q! ?4 A" s1 m mexPrintf("hello,world!/n"); ! c8 K3 V7 T* ]7 a- d
else
1 F2 k0 D! }5 {& I6 e mexPrintf("大家好!/n"); 1 ]! E: y9 v8 W7 m
}6 Q6 m7 a1 l1 T. U, I
5 n$ [, ^6 ?# k0 h5 S
4 e5 E4 {4 D) x将这个程序编译通过后,执行hello(1),屏幕上会打出: hello,world!
0 a' y# T8 a4 b而hello(0)将会得到: 大家好!; P9 H5 ^7 B0 Z0 R$ z# a
, H \" K8 ?8 q, S1 G用到了一个函数:mxGetScalar,调用方式如下:
1 Z5 D- B! C9 {+ O* h1 z/ M& [3 s i=mxGetScalar(prhs[0]);
* Y/ f) u! U. M"Scalar"就是标量的意思。在Matlab里数据都是以数组的形式存在的,mxGetScalar的作用就是把通过prhs[0]传递进来的mxArray类型的指针指向的数据(标量)赋给C程序里的变量。这个变量本来应该是double类型的,通过强制类型转换赋给了整形变量i。6 Z/ X: B0 Y! ^4 V9 x" g! @1 S' G
; ]/ ~& e, q- D5 X; e- r
3 h& w% u, Q. l7 B; `+ _
$ w, m9 S- c, i3 ~) s7 `* d: B//hello.c 2.1 1 C1 R4 D2 W/ p: M S5 w
#include "mex.h" 4 H: W, z( e. V8 I8 M
void mexFunction(int nlhs, mxArray *plhs[],
$ d* }2 K3 [5 U9 T, l3 K- Oint nrhs, const mxArray *prhs[]) 4 e0 j% h2 H/ b
{
+ O1 _5 Z" |2 `6 x! sint *i;
( ]0 t1 c+ C; p- e' [i=mxGetPr(prhs[0]); ( T7 b! ]5 L# `. Y* N
if(i[0]==1)
1 X" O: E: N" q mexPrintf("hello,world!/n"); 3 W# E: Y! A, F8 ?: W* j
else
9 F/ L5 q: V! v mexPrintf("大家好!/n"); / f0 [3 Q P8 N' M# D: y
}
1 K% f( r( {( S1 K; v' J; o" ^9 k; ^' ?5 ~. g6 r e
3 w0 N" p# M$ y; R/ {7 l) E* x; M- `7 G. J5 I
这样,就通过mxGetPr函数从指向mxArray类型数据的prhs[0]获得了指向double类型的指针。3 D9 W( k- j7 ^/ \0 _8 F
但是,还有个问题,如果输入的不是单个的数据,而是向量或矩阵,那该怎么处理呢 ?通过mxGetPr只能得到指向这个矩阵的指针,如果我们不知道这个矩阵的确切大小,就 / S' w2 t/ E. i
没法对它进行计算。
; m( g$ K& R! n, t( Q9 u为了解决这个问题,Matlab提供了两个函数mxGetM和mxGetN来获得传进来参数的行数 和列数。下面例程的功能很简单,就是获得输入的矩阵,把它在屏幕上显示出来:
1 N6 t5 o) p$ f `* [: A9 I" ?) h! \ i( m
0 u5 T. P6 ^# N! h+ j- V
//show.c 1.0
6 I: V$ [; y( z/ {#include "mex.h"
: r5 ?- S+ Y& }5 U2 z#include "mex.h" 7 o3 E6 \& h! W$ p
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) / X; E n( \( z4 ]) b
{ 5 [ Y' t: r) E( R
double *data; 0 \8 N6 b; I5 I" v5 }( \) h
int M,N; 8 r! X" A( |# P6 _ D
int i,j;
) ]* Z$ K% Y$ t* Adata=mxGetPr(prhs[0]); //获得指向矩阵的指针 0 l' m- v7 F) L: t- A* v
M=mxGetM(prhs[0]); //获得矩阵的行数 8 a9 U; @9 V* @8 F! {
N=mxGetN(prhs[0]); //获得矩阵的列数 3 c& B$ e* d6 W; J
for(i=0;i<M;i++) / j$ Z. k. R, G& z1 H
{ for(j=0;j<N;j++)
1 M' M0 H/ ~: r$ W mexPrintf("%4.3f ",data[j*M+i]);
5 o2 m; s) `# x' l; m/ u& Z mexPrintf("/n");
3 h! K, M4 T1 @: p' ?* q0 N }0 K! n- d) ]( \5 s9 ]" o/ D
}
j: Y4 r S$ x+ s& l$ }" A- \4 O0 {
) x) h: z* J7 m. D0 v/ m" r" } 0 c) {5 I# D6 Z) _
# K' O) k4 q1 p9 d# U% N编译完成后,用下面的命令测试一下: ; M; u' @" n/ n h T: L
a=1:10; + r+ p4 p$ h1 N) P% ]' B" j
b=[a;a+1];
. G7 C5 d8 C8 y show(a) 0 q. C/ ~3 R I; z& n. o9 h4 k
show(b) * A3 w2 o! j+ b O* N" ^5 I" u
需要注意的是,在Matlab里,矩阵第一行是从1开始的,而在C语言中,第一行的序数为零,Matlab里的矩阵元素b(i,j)在传递到C中的一维数组大data后对应于data[j*M+i] 。 " v' m# W8 Y; x+ a' {
输入数据是在函数调用之前已经在Matlab里申请了内存的,由于mex函数与Matlab共用同一个地址空间,因而在prhs[]里传递指针就可以达到参数传递的目的。但是,输出参数却需要在mex函数内申请到内存空间,才能将指针放在plhs[]中传递出去。由于返回指针类型必须是mxArray,所以Matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存的申请,函数原型如下: ( e8 `2 G/ ^, E0 Y& T0 W% }
mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag) 4 W* ^7 f0 ^; L% q) a9 @
m:待申请矩阵的行数 & h2 z9 b' N8 x% U6 T* {+ {
n:待申请矩阵的列数 & ?8 e1 {$ Z, r3 Q4 s' o
为矩阵申请内存后,得到的是mxArray类型的指针,就可以放在plhs[]里传递回去了。但是对这个新矩阵的处理,却要在函数内完成,这时就需要用到前面介绍的mxGetPr。使用 mxGetPr获得指向这个矩阵中数据区的指针(double类型)后,就可以对这个矩阵进行各种操作和运算了。下面的程序是在上面的show.c的基础上稍作改变得到的,功能是将输 5 u) W+ @3 c A/ C' h+ @
% O7 ]- k0 ~' }; T6 V: a3 h
* `% m" i3 z; h; M1 ], p8 e
' ^, v& d& F, e5 V- @) N3 v; Z& g% Q$ S, S& Z4 o. {+ L/ {/ T: e
//reverse.c 1.0 9 {5 E/ m0 A6 P( @: k
#include "mex.h" ; G( J6 T5 X- o9 }: S2 R: T! w/ r6 Z
void mexFunction(int nlhs, mxArray *plhs[], ~$ O. A; o0 I( B1 ^
int nrhs, const mxArray *prhs[]) 9 J- r M* Y, n! y
{ & _ _1 f6 c! w+ p+ L! N
double *inData;
) Y- A8 A1 M* tdouble *outData; & K& z/ p4 B/ J! L. c. p
int M,N; , @3 z4 E& _& U2 b" c4 g! N3 u
int i,j;
: N$ `% M r1 zinData=mxGetPr(prhs[0]);
1 H1 P/ B) h: T$ |8 MM=mxGetM(prhs[0]);
d! z$ H1 G& v# tN=mxGetN(prhs[0]); 8 ]$ W, D s. Z. K
plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL); 4 s# a3 ?. a1 s- Z- O: Z4 V
outData=mxGetPr(plhs[0]); ( K1 j* G. p& Y k
for(i=0;i<M;i++)
# T& [7 ?# B3 K for(j=0;j<N;j++)
4 T, @/ b9 Z1 C7 A( K: V outData[j*M+i]=inData[(N-1-j)*M+i];
, e% `" O0 G4 j c} $ r: c- ~! T: _& G: @* i d
* p/ S Y. V9 t; B1 J
8 @6 r# g/ R2 {
# s3 F& W q5 }, T H
当然,Matlab里使用到的并不是只有double类型这一种矩阵,还有字符串类型、稀疏矩阵、结构类型矩阵等等,并提供了相应的处理函数。本文用到编制mex程序中最经常遇到的一些函数,其余的详细情况清参考Apiref.pdf。
E( G2 v' n- _/ V1 P% y通过前面两部分的介绍,大家对参数的输入和输出方法应该有了基本的了解。具备了这些知识,就能够满足一般的编程需要了。但这些程序还有些小的缺陷,以前面介绍的re由于前面的例程中没有对输入、输出参数的数目及类型进行检查,导致程序的容错性很差,以下程序则容错性较好
# I. X* B) V) |( s/ I: ?" x: O
/ M2 d1 d0 n0 B
; q' Q: s" Q$ i. O2 Z9 y) H d#include "mex.h"
3 M/ p5 N/ u, T* X) l2 Ivoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
C# B1 Z8 g3 C{ , ]2 ~' `: R6 y
double *inData;
; v' p9 Y; @7 a9 Udouble *outData;
/ b6 D; [) a3 \+ ?' O+ F6 zint M,N;
3 `; p. C, R5 s* O5 v) ^//异常处理 9 h- R0 E9 [+ G% A
//异常处理
6 i' O! Y& U I$ g: x" W; ~+ cif(nrhs!=1)
2 ^4 a6 `; Q0 W C mexErrMsgTxt("USAGE: b=reverse(a)/n"); ( W( J2 e+ @! j2 n" g2 {
if(!mxIsDouble(prhs[0])) + y2 |6 ~" X5 D) k! m4 c
mexErrMsgTxt("the Input Matrix must be double!/n"); 1 d0 P2 O e/ P3 `4 ^. I+ t
inData=mxGetPr(prhs[0]); * O4 w1 v+ o- d, W
M=mxGetM(prhs[0]);
) J5 t) F" }( L) b, A' W5 o1 ]+ f N=mxGetN(prhs[0]);
8 `0 K: l( C$ _) \6 f% U plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL);
, K! M; Z$ u( U" @ outData=mxGetPr(plhs[0]);
! W# ^! y; I: ]3 c; x- [ for(i=0;i<M;i++)
! J! G% I2 A4 B for(j=0;j<N;j++)
/ h8 L& E9 ], T v5 y" i7 P5 F. d) l outData[j*M+i]=inData[(N-1-j)*M+i];
& y1 ?! w8 z6 ~ }
1 x9 i Q6 [( M$ C$ i+ Q/ L* @) c! K' b, ?1 O$ U; Y5 K( p# y
5 X- K( N) X3 p
) N. I& Y5 J4 d' ?. v7 V: v, S在上面的异常处理中,使用了两个新的函数:mexErrMsgTxt和mxIsDouble。MexErrMsgTxt在给出出错提示的同时退出当前程序的运行。MxIsDouble则用于判断mxArray中的数据是否double类型。当然Matlab还提供了许多用于判断其他数据类型的函数,这里不加详述。
8 q; I6 L" l, k9 k, x需要说明的是,Matlab提供的API中,函数前缀有mex-和mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mx前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mxErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。' k6 m+ k& b, P2 O* W9 ?
至此为止,使用C编写mex函数的基本过程已经介绍完了。
7 v0 R4 G9 H7 z- O! f8 P: l: X) y6 X3 M
|
|