|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
4 s0 S- l+ P/ U; m7 J$ f: `( a
通过把耗时长的函数用c语言实现,并编译成mex函数可以加快执行速度。
# V* k! ^. v/ L$ K9 ]6 w& m; G, g8 ?1 h4 Q9 B
Matlab本身是不带c语言的编译器的,所以要求你的机器上已经安装有VC,BC或Watcom C中的一种。
/ K1 r5 v0 N" E5 f H! V
- U6 k. U* v& N# q如果你在安装Matlab时已经设置过编译器,那么现在你应该就可以使用mex命令来编译c语言的程序了。
- X% S6 U' O0 Z# W( Y$ M
' C8 i% ]- W7 q' X7 G7 z# e# x如果当时没有选,就在Matlab里键入mex -setup,下面只要根据提示一步步设置就可以了。
3 E# R$ s5 S$ x" B8 j8 r! y( U/ s7 _% ?; I6 O3 e
4 R: I! ]/ c; [& ]
$ B G2 |: q! H1 N为了测试你的路径设置正确与否,把下面的程序存为hello.c。
/ r& k8 g" m; y1 I" \. h ^+ z& G" {& D9 R
?
1 _$ ~+ l6 x0 k) P; F/*hello.c*/8 `* D: b9 N( M0 \. t
#include "mex.h"1 }& J6 D, y0 E) K7 E4 a
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])* x$ Q( t& l+ |; X3 a; S
{ mexPrintf("hello,world!/n");% [! N* @9 Q6 H
}0 J+ v( J) V& v2 M- Q
假设你把hello.c放在了C:/TEST/下,在Matlab里用CD C:/TEST/ 将当前目录改为C:/ TEST/(注意,仅将C:/TEST/加入搜索路径是没有用的)。现在敲:: X$ P- g4 L% _) m4 A
mex hello.c
! D" z% A3 s; Z: y9 D如果一切顺利,编译应该在出现编译器提示信息后正常退出。如果你已将C:/TEST/加入了搜索路径,现在键入hello,程序会在屏幕上打出一行:
y5 l2 U8 P9 Thello,world!
* _1 h8 N4 H$ }* N3 I' G+ X9 Q. W- N5 h' ?
整个程序由一个接口子过程 mexFunction构成。
) b# m& T- c: }1 V4 O; [void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) % a. c; M: q5 i
前面提到过,Matlab的mex函数有一定的接口规范,就是指这
- t+ q" M6 H( W; Wnlhs:输出参数数目
# u5 l) x; y% H9 s0 @9 ~+ Uplhs:指向输出参数的指针
7 W! x7 u2 b- v( X0 q- nnrhs:输入参数数目
0 W3 d, {1 M6 l+ g6 S' F. r# S例如,使用
4 ?1 t. s1 F5 j- k# C[a,b]=test(c,d,e)+ _9 L5 K0 V8 T! r
调用mex函数test时,传给test的这四个参数分别是: H& p# \$ x5 b, G- D
2,plhs,3,prhs* n, ?% U. y# C
其中:
' T, i$ g, ]5 U9 oprhs[0]=c ' R% E) E2 l4 Y
prhs[1]=d
" O" T1 ]9 w U/ Uprhs[2]=e T$ i3 x; x% U9 F5 M8 U3 X1 b
当函数返回时,将会把你放在plhs[0],plhs[1]里的地址赋给a和b,达到返回数据的目的。
6 A/ \8 h5 D! Q1 d: S细心的你也许已经注意到,prhs和plhs都是指向类型mxArray类型数据的指针。 这个类型是在mex.h中定义的,事实上,在Matlab里大多数数据都是以这种类型存在。当然还有其他的数据类型,可以参考Apiguide.pdf里的介绍。
3 c' M' O1 X- m8 E0 \: m* F- J1 ` O
! `7 |; G$ |4 W, B0 [: Q& {/ }6 b6 I, C( e+ v
//hello.c 2.0 , e" Y( d% _, ~" t* K+ S3 i- \
#include "mex.h" " M" k+ g, O, c( C0 S# ~
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) W, H6 m4 y" g0 y
{
+ e& H: C" @7 ~1 X; p, w* ]8 [; Wint i; 2 Y+ f5 l' {3 j( m2 Z1 X, N5 p
i=mxGetScalar(prhs[0]);
: K4 d3 ~' J- R& Dif(i==1) 8 X0 N. L- ]' B6 [$ b" w5 Z
mexPrintf("hello,world!/n"); * }% E1 b, w$ H8 r* G* g
else
4 l" \2 m" I( f: ]: H mexPrintf("大家好!/n"); - z$ Z" y4 Y. ?, n. H3 X" R7 o- W
}3 |& V ], f' E& f+ y
+ R5 @+ L% j! D/ c- t; ~" |# L! E& _: T1 V+ o+ [5 H9 [
将这个程序编译通过后,执行hello(1),屏幕上会打出: hello,world! $ V# |2 l4 L( r& r8 q
而hello(0)将会得到: 大家好!
2 K5 E# E4 m& Y; Y+ B% ?) Z
7 M. X. c( i/ k" Q4 {0 H用到了一个函数:mxGetScalar,调用方式如下:
! O0 y& ]$ T* ?+ \6 A9 p i=mxGetScalar(prhs[0]);
% I) |2 }5 S. J; Q2 v+ ^ Q"Scalar"就是标量的意思。在Matlab里数据都是以数组的形式存在的,mxGetScalar的作用就是把通过prhs[0]传递进来的mxArray类型的指针指向的数据(标量)赋给C程序里的变量。这个变量本来应该是double类型的,通过强制类型转换赋给了整形变量i。
+ |' l; G( P2 b) W( ~0 R3 \# T, V
; B8 r! T. G+ p: S( R
1 r6 n+ c" w3 P d* D
" }' n1 R; `4 J- r4 l8 w3 z J//hello.c 2.1 0 M* U. {4 E/ w& i, G0 r, D
#include "mex.h" / \5 L' t- A* \7 Y! V
void mexFunction(int nlhs, mxArray *plhs[], 4 X# b: H& X. v; k; r: n
int nrhs, const mxArray *prhs[])
9 Z% L; O" K$ Y7 U{
" j2 ?6 D) p) I0 x8 C- R+ m0 W4 M- r" J+ Pint *i;
6 W D1 M. i3 L4 i6 }2 ii=mxGetPr(prhs[0]); " P" A* ^& }; T( k8 S. L
if(i[0]==1)
/ m- H& u. F9 I" y* b# V, ?; ? mexPrintf("hello,world!/n"); ( |. u9 A3 d8 N( ]5 E- \( A
else
2 W0 K$ g9 E: ~2 I mexPrintf("大家好!/n");
+ L0 q0 O5 H3 E5 w4 _' b}
3 J i& I$ u2 }& p2 K/ V$ J9 s& t0 N" R
! _* K5 O/ u& n* w: N8 n
2 g! _4 r9 l1 l3 a! `! x这样,就通过mxGetPr函数从指向mxArray类型数据的prhs[0]获得了指向double类型的指针。) u+ R$ l& ^0 q! F6 M3 f
但是,还有个问题,如果输入的不是单个的数据,而是向量或矩阵,那该怎么处理呢 ?通过mxGetPr只能得到指向这个矩阵的指针,如果我们不知道这个矩阵的确切大小,就 7 T( F; F# n7 q, E+ z, r. C
没法对它进行计算。 $ z, V$ x1 G2 A+ @' {
为了解决这个问题,Matlab提供了两个函数mxGetM和mxGetN来获得传进来参数的行数 和列数。下面例程的功能很简单,就是获得输入的矩阵,把它在屏幕上显示出来: . \# B) z6 r" t& X8 r# ~
: h3 z* s q2 c _ o$ |8 i+ U
9 Y, W, ^( q! C" I//show.c 1.0 6 {) p. S. |0 l. G- L1 A
#include "mex.h"
6 h: }/ ^( w, R. J* v! g: E+ E+ V#include "mex.h"
; x7 S- V' G5 f6 C0 W- P3 Qvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
; c6 z& L N; E; s1 a{
+ i6 l' |" x+ O9 y$ L: Hdouble *data; / e3 ~' r, n; b; z; n7 P
int M,N; 3 U3 K1 I5 f9 C9 e! Z O3 [
int i,j; * J) ^' c" t( r+ z" I( G
data=mxGetPr(prhs[0]); //获得指向矩阵的指针 . |& D4 S' J3 _1 |0 g- D& a' u2 ~0 B
M=mxGetM(prhs[0]); //获得矩阵的行数 # u6 W7 Z4 I6 q2 H; ~3 a
N=mxGetN(prhs[0]); //获得矩阵的列数 m8 Q' f* S) ?0 Q
for(i=0;i<M;i++) & P( X5 I: _- D3 u* R
{ for(j=0;j<N;j++)
: w4 _& t5 k* K" j3 ` mexPrintf("%4.3f ",data[j*M+i]); 7 [/ G/ F/ n" _+ k. @
mexPrintf("/n"); 4 f4 d" l5 _! x+ c' ~; X: u! M
}
- r( K) n/ i. N m, T+ X1 N5 y}
9 j& }- \$ C% ?& f% g9 h) i. T1 m* d* u Q; D. m
$ F& y8 N# n5 x% j6 H1 I) j7 p
& D/ r" g: V) r/ z- \$ `- n编译完成后,用下面的命令测试一下:
! i$ q, o, R% R* _' }# |# u" ~/ u a=1:10; 2 {3 l; c+ y% `% n7 D: a
b=[a;a+1]; % p6 h/ B$ ^% {
show(a)
3 i5 i9 @! R( n$ d show(b) 7 M6 ^6 Y; j% T* `. F% n
需要注意的是,在Matlab里,矩阵第一行是从1开始的,而在C语言中,第一行的序数为零,Matlab里的矩阵元素b(i,j)在传递到C中的一维数组大data后对应于data[j*M+i] 。
5 a2 e4 k+ y: E x7 m输入数据是在函数调用之前已经在Matlab里申请了内存的,由于mex函数与Matlab共用同一个地址空间,因而在prhs[]里传递指针就可以达到参数传递的目的。但是,输出参数却需要在mex函数内申请到内存空间,才能将指针放在plhs[]中传递出去。由于返回指针类型必须是mxArray,所以Matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存的申请,函数原型如下: % |! l$ r% B4 d8 s) Q" N9 @
mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag)
* q _; ~) E9 z, a$ ? m:待申请矩阵的行数 * w7 U$ S+ E& @* P4 q. G
n:待申请矩阵的列数
( z. n, S3 G V1 Z为矩阵申请内存后,得到的是mxArray类型的指针,就可以放在plhs[]里传递回去了。但是对这个新矩阵的处理,却要在函数内完成,这时就需要用到前面介绍的mxGetPr。使用 mxGetPr获得指向这个矩阵中数据区的指针(double类型)后,就可以对这个矩阵进行各种操作和运算了。下面的程序是在上面的show.c的基础上稍作改变得到的,功能是将输 l: a# z5 n1 y; w) D; Y/ n
4 R* s( q" `3 O& R/ _
) G' H5 Z+ M( R3 }, M5 Q
( [" v* x! o8 n- h8 c; Z& }
L: ?- E% K5 J" J/ i! \' j
//reverse.c 1.0 & z2 o' _, M* g- l
#include "mex.h" 4 t% E5 G7 G5 S- S- {4 x
void mexFunction(int nlhs, mxArray *plhs[], 7 ?! [+ O5 d" I B. s
int nrhs, const mxArray *prhs[]) $ U e% N, J" v7 z6 H. t0 n
{ : E: [5 b8 _- d& ~- l! W; Y
double *inData;
0 p: ~1 F, t |$ L" C" X9 b( _double *outData; ) h5 [( x5 V0 W
int M,N; 7 g' C1 Z3 |1 O2 z* X+ q$ F& Q
int i,j; * | g6 Y* d& i
inData=mxGetPr(prhs[0]);
6 u" ^1 b+ H; @2 T; H% XM=mxGetM(prhs[0]); ) P8 d! F, t3 ~6 v
N=mxGetN(prhs[0]); : m1 H: r: k) ~( E& i+ W
plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL); ( _5 v( f# N6 [9 D2 v
outData=mxGetPr(plhs[0]); 7 E b$ M9 I( }# X g! w' D6 a& D
for(i=0;i<M;i++) ) Z1 @) l- L4 c# m7 K$ R. W
for(j=0;j<N;j++)
& P1 h% z6 ~) I9 ?3 O; o outData[j*M+i]=inData[(N-1-j)*M+i]; " R& a6 {5 |0 ~3 A
} 4 g5 l! S" a! x1 J
# W$ J# P3 x/ C4 p; H) f$ {' I, j
- |0 q: u$ u& @
[" J5 @$ g5 u4 j; l+ ~
当然,Matlab里使用到的并不是只有double类型这一种矩阵,还有字符串类型、稀疏矩阵、结构类型矩阵等等,并提供了相应的处理函数。本文用到编制mex程序中最经常遇到的一些函数,其余的详细情况清参考Apiref.pdf。
* N( J7 e4 H4 c" W S通过前面两部分的介绍,大家对参数的输入和输出方法应该有了基本的了解。具备了这些知识,就能够满足一般的编程需要了。但这些程序还有些小的缺陷,以前面介绍的re由于前面的例程中没有对输入、输出参数的数目及类型进行检查,导致程序的容错性很差,以下程序则容错性较好
$ m% m5 \- }& r3 Y8 o% q, x. v$ _2 c1 c, z
1 e+ S" o3 v) ^" c8 V7 S# _
#include "mex.h" " |7 P I* b0 |- B+ x
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) & H, x3 F/ `' y9 E' c1 i7 y
{
: x& h }6 R) s( hdouble *inData;
* S" P% A4 ^1 E- C* q; x4 ldouble *outData;
1 Q' I4 \3 |) U3 u* L9 p2 c {int M,N;
9 U& y7 z9 @9 }//异常处理 ( i7 W- z* i2 K# V" E# j9 n
//异常处理 : P' t* e) J$ { }4 R
if(nrhs!=1) 2 T& O% }% v( n/ ?6 q
mexErrMsgTxt("USAGE: b=reverse(a)/n");
! @- W& R H* T6 f8 O4 c/ x if(!mxIsDouble(prhs[0])) / t' ]1 j) T S6 p% {/ o
mexErrMsgTxt("the Input Matrix must be double!/n"); $ | C0 [$ [3 F; ]
inData=mxGetPr(prhs[0]); % `6 k; r8 ~" z u9 T/ ^* L3 c
M=mxGetM(prhs[0]);
, x* y" Q8 i0 q3 e2 [' n N=mxGetN(prhs[0]); + [# `" ]2 ^( \+ s% ?8 k
plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL); 3 `4 J- V- Q3 j, @
outData=mxGetPr(plhs[0]);
* |( |# H h5 w x/ I3 y for(i=0;i<M;i++)
' h9 z% M" M) e3 Y2 k4 o6 h for(j=0;j<N;j++) 3 o3 h% w# x3 {2 {( d2 J$ t1 w$ }
outData[j*M+i]=inData[(N-1-j)*M+i];
4 {; ?, `8 A+ M- k- n& |% s }
( A0 o" g0 F( X1 r' b4 _+ _5 w. e
, Q t$ ~3 b6 e. @ y+ Y8 W7 t ! _2 E0 @6 R' o9 u( N1 x( h& r( t
; Z1 e x# D6 l3 F2 I5 }. B
在上面的异常处理中,使用了两个新的函数:mexErrMsgTxt和mxIsDouble。MexErrMsgTxt在给出出错提示的同时退出当前程序的运行。MxIsDouble则用于判断mxArray中的数据是否double类型。当然Matlab还提供了许多用于判断其他数据类型的函数,这里不加详述。
, _. k( c- A9 @ S9 R6 f7 O5 R需要说明的是,Matlab提供的API中,函数前缀有mex-和mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mx前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mxErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。4 d. Q3 `$ G& O8 B
至此为止,使用C编写mex函数的基本过程已经介绍完了。 : P, ^, l0 u- V9 R- G% c
+ p. H9 K$ X7 A3 ]! U |
|