EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
前言:众所周知对于循环这种运算,matlab的运算速度是不快的 想起来个冷笑话,黑客帝国里的主角呢奥之所以能快过子弹,因为Matrix是matlab写成的hhhh 因此将循环部分用C语言写,然后编译成matlab可以调用的mex文件,无疑可以加快循环运算速度,从而加快整个代码速度 之前因为合并数据需要不会数据库,强行在matlab里利用类似excel的vlookup功能,无奈太慢,只能想办法最后知道了这种方法,最近偶然在桥哥的知识星球里又提到了,也顺便复习和记录一下。 ps:今天在闲鱼上买了小车车,高兴! 1.环境配置我的是matlab2019a,win10-64bit,c语言编译器我选的是TDM-GCC(gcc/g++),安装起来很简单,推荐matlab2015及以后的选择这种,详细安装过程可以见文末链接1中的For Matlab 2015部分
$ u9 t9 @& d9 y5 ]# O7 {2 E5 b
2.如何编写可以被编译成matlab可执行文件的c语言代码我是在matlab中文论坛(地址见reference第2条)里找到如何编写的,不过他写的有点乱,我来消化后写一下自己的理解 mex接口函数跟一般的C语言文件主要是两点区别, 第一是必须引入头文件mex.h #include "mex.h"第二是多了一个叫mexFunction的函数 void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])举个栗子:
4 Y! l3 k7 w7 ?* a* D5 O: S1 \, n* m
#include "mex.h" // 使用MEX文件必须包含的头文件
; O0 c7 z2 o$ U- m+ P' u( I, J5 N& H' w
- 0 e( @0 v) [- t, ~) i
2 ~, c+ A6 K6 x, g// 执行具体工作的C函数
: ]8 n& ]' _+ D1 L& F# Y, R4 N
' C# `) j2 b+ @ - 6 m8 H7 i, G8 l# l# ~' X
4 U* P5 w0 s8 U, p
double add(double x, double y)
% i# b& {8 R9 D- z0 V/ A. s, c7 p/ p- k. G
0 @/ c- H, m" Q' C; b/ ~0 ]( N+ I$ _2 ]& y
{
4 d3 S7 j& [! U: p/ Q1 ~* _! D1 w, e1 A' v5 P9 P
- G; D5 E( K4 M9 J7 L; |
: x4 Z+ i6 F2 x$ u7 h# b return x + y;
6 ~# G; u. ?! g" n9 |6 N. T1 o9 \8 ^9 Q" `( y" A/ S" t
- - K1 \, w& \& Z: L2 H; j0 `" o; {
- g6 Y% ]& W. W: T2 E ^3 F- s1 e; ^
}" r- b) |% }, J2 t, s& x/ V
$ o# E, I: k6 Q" u4 G% j - 4 [. } t8 P2 p( _, J% Y4 T
, ]! [- v9 R. k5 ^, T' m9 ?// MEX文件接口函数2 q8 ?6 O% B+ K
$ ]0 b0 F' M* J7 W4 i
- 7 s, @8 i/ J# @" ]) S
& N2 R- B7 a: r4 u; Wvoid mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])
, z. \8 X3 S8 {5 M& @! y- M% k1 ^/ j: _0 M' b+ x5 ^
- 4 N& `& {* W% H1 R4 j. r
, O/ Y$ w% A3 [0 K( k
{2 l5 ?7 Y2 s0 o9 A9 D
, A+ o7 z5 a) k! `. Y+ X
- P+ a: w8 D J+ ` d' |' m: I: c% w% C a' }. a
double *z;+ Z5 W( l( [. b. l
$ h. T4 P9 G1 p6 X& U3 u
/ U% n: g$ G, |+ ?! H$ y* W1 p
4 a5 _8 k N6 e& }6 x7 q5 Z5 R, r double x, y;
! I* R" r) w) x) s7 a! T! _0 D0 u6 Z; H
; Z2 Z/ Y) L9 B) i T, R7 {
/ G% ~( B; M4 v0 P# ` plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
. J! O4 G7 {; }8 q' Z
2 z# }* I2 b1 K4 Z K$ q+ ]- & T: T1 y2 ]* `0 `( v( M
6 Q7 f: W) M! h/ f1 z8 ~
z = mxGetPr(plhs[0]);% W: L$ [1 I. W7 C* G7 Q q
, k' R- y( `$ B! H. z
- ( B( p* Z( b& c( V- S' y% z& M! i) d% D
: Q6 m3 e% a0 |+ H6 h& K1 u x = *(mxGetPr(prhs[0]));" k( p0 q; O* P' R. V
3 {& L/ X' Z9 _1 `6 Z E
0 Q" o; }- O* i& d
( v9 p& c) K' D! h$ g y = *(mxGetPr(prhs[1]));
6 V5 E M/ s; y! L! X6 Q9 l) r
6 Y+ p! I/ n# Y+ q' ?' p) @- b- 8 J) Y X' G" O( Z
- p6 V8 d. L& G- M$ z+ n- s *z = add(x, y);
$ @5 s/ g1 T3 A. f/ f% _& }/ j2 d0 x( W& q4 T4 ]7 ^# U* C
0 D7 S3 l" k2 C0 a% X' z2 r" t* w- K$ L$ }* H4 z
}
2 a7 O& O6 G$ d0 g
7 z3 z7 Q+ J5 J" u$ A0 p( _% U; H
也可以写成:
$ b1 F. h8 S# T# V: M `- F+ o8 [, o$ }3 p4 A- V
#include "mex.h" // 使用MEX文件必须包含的头文件1 z p+ d5 K7 r$ s0 p
5 R; ]' `1 r# x1 S/ r- & } f2 S7 Z+ i
$ i& h0 K1 l" Q- ?
// MEX文件接口函数
# \ }- c4 N; v( C6 U
) O7 H7 `+ W* f8 p, I Z
: ? J2 J9 I Z( @% v+ w. I: a' O) I
void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])3 ?5 O- {, ~" Q
$ y% r5 }: y5 a
- " s6 [* v+ u3 A0 U, A0 S2 R
4 U, j2 b- N7 |4 v{
/ u% e# b# q8 o1 Q2 e8 H3 R5 f& l" c* q3 Q& Z0 I! i: b( }
- ; Z" j( w/ J, d' }) L
- V% V+ J# R- o5 d! O& b9 U double *z;
- L. T% ~* N9 o
0 G1 Y3 s+ b4 a, o) u$ `) P - 6 g h. h5 U O' L3 D0 H
3 \6 X, k* h& N" g0 h" B" `! b
double x, y;8 p; L" E% p# k/ Q& \. D! J
" s7 ^' s) u* z @: K) r; G1 a - $ U- M I0 x4 ~6 Y# B. @* }
$ [9 T, a) c* i$ z) v
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
* Z* i2 P, F i0 d- Q* A9 q9 i2 O) Q1 Y+ s
- 4 s& k- A2 a8 n" S: @$ H6 t
; j- b1 @9 [" _% r( E" F z = mxGetPr(plhs[0]);
/ Z% b+ z+ ~, ~' d+ x7 ?1 L( a3 |% Y( w) B6 x
: [* F4 u& F" ~* G m6 W! f$ I4 a3 @: d; l$ _- x7 T
x = *(mxGetPr(prhs[0]));
' A2 J+ Z, V. |9 \1 d, Z6 q) g' ~5 N% `- y0 M. r
- + L& E( d! f @4 Y& o
' Q$ y5 Y9 T8 h y = *(mxGetPr(prhs[1]));
; k: L0 X2 j5 U2 b- t/ Y7 J7 o/ m, i& o. q
0 G, X( x8 ~7 X, j I
7 F2 f9 n, i5 x) h" D, e' }9 _ *z=x+y;
9 k- A: ~& c+ X. ~3 p1 _
0 ~% C$ x, U" D$ ?9 h, b9 u- % N1 z, u9 R$ @# y. Z
& b% v# \: M, O+ n/ s. L, v2 Q2 o}: b3 }: [) s, e7 G9 p
p5 x' I" Y: B3 m1 L0 c% F2 M, S
' J$ a. B+ ]5 [+ s- g/ Y; k0 r
也就是说执行具体功能可以通过编写一个独立的c语言函数在接口函数里调用,也可以直接写在mexFunction接口函数里 我个人推荐将具体功能独立出来,这样一来: c语言函数负责执行具体功能 mexFunction接口函数负责数据的输入输出(将matlab数据输入c语言环境,运算后再输出回matlab)形式组织 再调用c语言函数
分工明确。 于是关键问题就是如何编写mexFunction接口函数了 void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])下面解释一下mexFunction各个参数的含义(这里看到是void,即无返回值,因为传值是通过指针数组plhs[]传出的) 参数 意义 英文全称 类型 & o9 a( }& |: F' k/ ?$ b; |
nlhs左边输出参数的数目number of left-hand side整型(int)3 N, v$ ~. y: Y5 v* `
plhs指向输出参数的指针pointer of left-hand side指针数组. a0 @3 `" l2 g
nrhs右边输入参数的数目number of right-hand side整型(int)
7 ]8 S! B% a# l- n4 ]/ w8 Zprhs指向输入参数的指针pointer of right-hand side指针数组
+ `$ o2 u" ]+ h& B y- ?8 _ x第一个参数nlhs是输出参数的数目,第二个参数plhs是指向输出参数的指针 第三个参数nrhs是输入参数的数目,第四个参数prhs是指向输入参数的指针 其中plhs和prhs类型都是指向mxArray类型数据的指针,prhs这里还加了个const,这里的const跟之前c语言中是一样的,代表不改变,因为prhs是指向输入参数的指针,mxArray这个数据类型是在头文件mex.h中定义的,in fact ,在matlab中大多数数据都是以这种类型存在。 还是拿之前的栗子来解释: - * Q- Z8 u- s, F' `; o
+ b k% ^5 X+ X) v% I. B7 Y$ K0 z' \#include "mex.h" // 使用MEX文件必须包含的头文件
/ E; w7 s$ e3 w# ]* x6 c& |) F) a2 \$ k/ i7 b& p: k: B/ p; U
; u3 b( I# u4 T! p9 [. D: D+ P
_7 X0 p! I( C5 y- w2 ~$ g// MEX文件接口函数
- W Y. C' c% V% i6 h/ `/ E4 c0 |- a0 h# P E+ N
. w$ X* H& Y; i L* L
6 B6 Q ^" f. {void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])
% R2 R9 c3 R7 z3 u7 F% X$ ~4 r: l' D5 O% H: _0 E8 J* H
- * [% y7 V% }. }3 P
+ [% i1 P2 D8 S5 p$ }
{
6 e% Z5 Z' ~" p
# i$ { f" M8 ]4 G1 f/ d8 M2 _7 S - 0 e6 e% n' _, v) V2 M4 _' Z
! u" ^1 U: R( {6 p0 _0 ~
double *z;, z, r/ L' h( K+ x" c8 h) [/ A" V
6 {7 w/ S1 H( r- b$ }
- & R" n7 [' R7 F$ ?6 p' k
9 u/ e: g" C. ~8 B5 v3 S- |
double x, y;
/ I, t4 E ~& v! R* B* M
0 H; F% Y0 [" f3 B) { - 0 F+ d! G; \2 \/ r# t
- `/ S) a1 H; C4 F5 W, r$ u plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
" A) v" L* X9 s+ n8 m8 x4 {9 e# S2 l8 p6 F3 s
" j6 M& g- ]7 l7 G4 ?
* d* Z$ \: B6 R z = mxGetPr(plhs[0]);! ?7 x9 s- r' h
* ~3 r2 h8 J' Z' \$ }$ l
. ` H2 J) s8 _% i) M9 B3 K) A R8 X1 O+ u1 R
x = *(mxGetPr(prhs[0]));2 k8 ~3 s3 P! U8 u6 A" M; n
2 ^# _1 u8 g# Q+ q% W6 Q
# ?* ?8 F1 A4 g* N4 U! T8 z
: O: W) |: K2 a4 }# Y! `5 w y = *(mxGetPr(prhs[1]));6 g. Z7 M6 l7 T# R$ p" E" e! |/ W! n1 ]
g( |7 R7 r. U" U
- : Q- i/ O) X$ L+ z! u; o; z
; Z! Z9 s B& j: N *z=x+y;
% E0 X; j) G6 x+ M' _% j5 x
( p( y" w6 f8 N1 R) @ - 9 [2 Z4 E2 X5 B- l! T
! t) s) n8 E6 g9 T5 \- e4 X. Y8 M; u
}- L0 f4 S5 r* u/ S$ ^
; C7 a1 v0 P/ U# S- n* v
+ F8 g$ g, y" K- d* g
在这个栗子中,输入参数是x,y,输入参数是z(虽然没有返回) plhs和prhs其实都是指针数组 prhs[0]代表指向第一个输入数据的指针,mxGetPr代表获得这个指针,后面再加个*就是c语言里面再正常不过的按指针取内容了 所以就这样x,y通过下面这两句传入到了mexFunction中 - 5 B) A9 \; d J6 w8 u# m' T" E+ U
5 Q! P+ C- _: H( S- N! G9 T
x = *(mxGetPr(prhs[0]));
) k' _; N# q1 S* b- @0 G$ h% F0 k5 q9 A6 h0 ]0 f" m
; x+ o+ L2 c- M0 ~) n! o% ~. n( E$ u. X# g y
y = *(mxGetPr(prhs[1]));' M; u! r8 _6 L7 u1 ^
P' ?& f E* g5 J/ N9 u4 U' x, Z
$ B" Y. G- U; ]; G
mxGetPr函数的功能是从指向mxArray类型数据的指针prhs[0]、prhs[1]中获得了指向double类型的指针 另一个函数是mxGetScalar,Scalar即标量,功能是把通过prhs传递进来的mxArray类型数据的指针所指向的数据(标量)赋给c程序里的变量。前面的mxGetPr是传递矢量的,否则矩阵或者向量就没有办法传进来 这里由于传入的每个参数是单个值,于是也可以用mxGetScalar改写: - 3 ^: g B# q0 c
4 i4 }( ]7 @! T* g: Q- H#include "mex.h" // 使用MEX文件必须包含的头文件
1 `5 Q: ^ {4 E w
+ l- {1 A4 o# p! }
: K' D' O8 \( @" F; ^
( x7 W s0 ^( R$ J' k// MEX文件接口函数
" E* D: w1 W S5 {* F) Z; k. Y
9 N% n+ N9 H6 S0 X- e
8 H x4 i' _2 ~3 @( P3 u( ?
$ l$ p) m4 `4 g! D+ Evoid mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])
1 a1 N6 I. Y- [2 r2 w8 T: M* i1 R% h: A+ L- G" X/ F
7 S1 a( ]) j1 `0 J0 L
3 |! Z2 }) L4 ?' ?3 q" Y, b{& \' T) B2 E1 N c. V& [% d v
" H- Q* l# v/ |/ `& F# l
, }( {. j& W, B! p4 V/ s* |2 U! U* p$ J0 {( L' U
double *z;
' M% s8 k+ H# e$ e* `
) \" X0 H; n. _) P( K, F3 t
* F+ K) u4 `# H7 u" x
. n+ ~! E! |/ t$ x double x, y;9 N* B) T3 R% m& f5 r
' I% H2 U" O2 N1 i
- . D5 D) V% V8 }' W2 x
7 d6 ? z3 o$ G* B4 h, \8 ?
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);: N: I5 u ?* _
$ a# a& p6 B- ^) _1 M: { - - ~* K9 _, Y" p. X9 l& {
+ @- a0 h2 r3 k' ~
z = mxGetPr(plhs[0]);
+ C0 S* ^ o# N8 x+ O: Q: t- v' Z' V$ ^ j$ F o* X
7 A! E0 @6 C% b5 b- Q9 o/ z9 G2 N6 N- O: j$ p1 Y
x = mxGetScalar(prhs[0]));
% l5 @ X& x0 c* i/ C
2 `: T' y- f! [- ) |8 l& Q% x6 o. C
5 g! k6 C% F0 m; N5 \! J3 q y = mxGetScalar(prhs[1]));" f) @) T$ S1 I! \0 N
# F1 U9 q2 b! S' o7 R& o - " X1 e* d# m9 ~7 Y/ K
3 c- w$ s; u8 f
*z=x+y;
6 B! L( t1 ?. E6 Q. v0 l6 g7 Z! B; V) @* ?8 `6 p0 X
, ^% h7 [& |+ G' }4 [5 g; w- O8 t
% ]0 O7 g, h% e) D. z. P4 S* z. m" m}, D4 i( p7 Q2 S% D: o6 @; W
% W' \( G5 Q5 `) R5 F
' I2 y9 ]5 @7 a, a+ {; x) C& @
(注意:输出参数因为需要通过指针传出,因此这里z必须用mxGetPr这个函数来从mxArray指针获取double指针) 但是这样还是有个问题:如果输入的不是单个的数据,是向量或者矩阵,即使我们通过mxGetPr获得了矩阵或者向量的指针,但是我们假如不知道矩阵的shape,还是不好取值或者计算,所以这里又有两个函数mxGetM和mxGetN可以通过传入参数指针获得传入参数(矩阵)的行和列数。 需要注意的是: 在matlab中矩阵的第一行和第一列的序号是从1开始的,而在c语言中是从0开始的,而且在c语言中的一维数组存储矩阵的数据时,是一列列从上到下,从左到右来存储的,也即 matlab中一个m*n的矩阵M(i,j)对应于c语言中一维数组N中的N[j*m+i]
举个栗子: - ' J8 J/ F% ] l: V, i
" r6 b V1 t1 C
#include "mex.h"
$ a, C! K2 X, U! {: y7 y: }8 \) Z+ X+ F P: r. g& R
5 H+ r& s8 }9 s% M X2 V% b; N! K. V/ f0 l
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){
+ w# ^! |6 D9 t
6 e2 g6 {2 U* e
* V+ s7 J" M% P8 }1 C* K8 |
: i& @" b7 t1 b, R1 L) ~/ ]! o& a
/ s4 M1 K3 i6 @
6 H0 E( F. L6 b/ R( c, V/ p& X, u# e. Q
double *data;
0 Z6 } v# o1 J v3 k. U
; k+ R- e/ {5 f5 K+ p- $ [7 | E/ b9 R1 n7 v
9 K$ @3 d k. r. m+ C, C2 l
int M,N;
U% y6 w. @' ?: f( n: I% \2 F, _( a8 l
- 1 s: q r9 n+ c& k& b# ~( n( H4 u
5 ~( l5 J0 z* V6 Z tint i,j;. J9 H3 b, K: \0 N
9 B2 E) q4 K& l - 4 ]- ?3 h; j" r/ `; g0 d0 z2 E. z
, }$ o- H5 m9 R: \( Z
data=mxGetPr(prhs[0]); //获得指向矩阵的指针
2 x4 m. k1 N9 R, ^0 x& z8 I3 p/ c& h% d# Y- Y' b$ \
# B, N- F/ q1 u
1 A$ `) s6 B; a: LM=mxGetM(prhs[0]); //获得矩阵的行数+ N% |& G- D" H; i/ u. c6 O
& K N) C: ]" A. C. L
6 _! z4 n n& p. B$ J. E& h
- `9 {& K% \; \8 VN=mxGetN(prhs[0]); //获得矩阵的列数( \3 Q6 K# c+ R2 t8 D1 U
) x6 |" J) i! h9 S2 p* p8 \
- ; ^4 S' ?- `2 E2 l% s
/ W$ k1 \7 q- Z( X; q$ X7 p9 h' R5 pfor(i=0;i<M;i++)
: d! Z* a" j" W' t$ [+ v1 i9 o! P) ^8 X
- 4 Z. y- X. e" W$ r3 d, z
; _$ q X7 U: K u: O
{
( ^: V9 C+ ?2 N* s7 A: i& w: c& b0 ?- E: a2 _% U. v+ w! k" @
, [8 y2 J& X! G4 {% \" ^
+ T* _2 I6 w+ |' X I5 O for(j=0;j<N;j++)! [) ?; P* N* {8 w5 y
+ S* `: c L2 \! c/ ]* Z: B4 S- L
% l3 `7 }$ L# E" F# _; K2 ~8 |" j; m @4 ]2 u! E
mexPrintf("%4.3f ",data[j*M+i]);6 b% c ~8 N+ y* h
) r+ ]5 z) c3 g7 F# B; l4 {
, A, }! V, V4 i% C; D2 |. W3 L& H" I- K) S: {
mexPrintf("\n");
/ D! P T8 k; t0 a7 K* `. P9 d. G6 z7 {6 o. i
+ t3 O' j2 m0 v* t7 z7 F1 V: B$ v
8 g4 `- G2 Y* L/ B# L+ A}" p+ r! P- [2 Z9 E' E
, }" _) z4 B, X' h9 j, r- $ i0 T) \9 L% \
; x% l6 @0 E! z, z. b* o3 |8 Q0 g
}: w" H n: C* R9 c
) Q, ~' C4 F: Z# G
% C8 l- H6 ^* ^# i9 Q
假如是一个形如[1,2,3;4,5,6]的矩阵,则执行上述后会先后打印出1,4,2,5,3,6 以上讲的都是输入参数,由于输入数据在函数调用前就已经在matlab里申请过内存,由于mex函数与matlab共用一个地址空间,因此通过prhs传递指针即可传入输入参数。但输出参数却需要在mex函数里申请内存,才能将指针放在plhs中传递出去。由于返回指针类似必须是mxArray,所以matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存申请,函数原型为: mxArray * mxCreateDoubleMatrix(int m,int n,mxComplexity ComplexFlag)m,n分别是待申请矩阵的行数和列数,需要注意的是为矩阵申请内存后得到的是mxArray类型的指针,就可以放在plhs[]中传递出去了。但是对这个矩阵的处理(包括赋值)却需要在mex函数或者c语言函数中完成,这就需要通过前面的mxGetPr或者mxGetScalar。使用mxGetPr获得指向这个矩阵的double类型指针后,就可以对这个矩阵进行各种操作和运算了。 还是拿这个栗子讲,这里由于输出是一个数,于是m,n都是1,通过mxGetPr进一步获得指向double数据类型指针z - , L9 l7 `) E0 s- y4 c; u" f
# o* W5 O+ k) Q9 b* T! }& h3 C. J i0 @& [0 q3 o# F
1 `. M5 W; h) v, O/ m( M& X% U
' j w. x8 t+ W) z0 V/ L% v
9 M% ]$ x Z- V% e' c double *z;" b* m; {9 y$ |' e8 B z r; U
8 }. _4 ~7 U% _; s
' y: s, ^- n- t) e4 O7 E( X; _- {+ U0 U5 m& M5 y% q" H2 {
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
+ Y4 L: D. e. k# ~) V' J @! R* d' z5 {8 G& x0 l5 b' N
- 6 r( S6 x; ^. D$ F
; \4 v$ m' j& S! J: f z = mxGetPr(plhs[0]);8 x- m# Q' `& a$ Q" h$ l; z
5 h9 L8 J# J1 w: Z
' n1 E1 r" z' q! S1 E6 P
6 e* y6 Q! t6 r9 i) I" O x = *(mxGetPr(prhs[0]));
, ]* Q) g+ [ V" t, ^) S0 q/ ~% M7 p- l# g7 L
- 1 y/ @! B, G/ d* C, I
$ d* B- u# k, g y = *(mxGetPr(prhs[1]));
2 y8 r) V3 Y/ n; ] f% i/ b/ p$ g) G: G# ?+ K4 b+ z
! y. o8 `8 q( W8 Q% H1 c6 r; h' M7 G% {5 K* ~9 [' \
*z=x+y;. W7 ^, t0 x8 K( ?" h9 \
0 b- T1 d3 i( u O9 r7 k5 k! {: N% q' x/ K
当然,matlab里使用到的并不只是double类型这一种矩阵,还有字符串类型,结构类型矩阵等,并也提供了对应的处理函数。 ( v: I6 q3 y' W
|