寫MEX程序其實就是寫一個DLL程序,因此你可使用C,C++,Fortran等多種編程語言來寫。編程
編寫MEX程序的編輯器可使用MATLAB的代碼編輯器,也可以使用本身的C++編輯器,如VS2008等。數組
用MATLAB的編輯器的好處是,MEX函數會加粗高亮顯示,這給程序編寫帶來便利,惋惜沒法動態調試。如用VC便可編譯也可調試,比較方便。mex的編譯結果實際上就是一個帶輸出函數mexFunction 的dll文件,因此會用VC編寫和調試dll,就會用VC編寫和調試MEX程序。架構
#include "mex.h"編程語言
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )編輯器
{ide
}函數
四個參數分別用來輸出和輸入數據: nlhs 輸出參數個數,plhs 輸出參數指針 (nrhs和prhs是輸入參數相關的)。測試
注意: 咱們對輸出和輸入參數的操做都是經過指針的方式進行的。(這點很容易理解,由於咱們的計算結果是須要傳遞給MATLAB的,實際上咱們傳遞的不是數據,而是指針。MATLAB能夠經過這些指針,訪問內存中的數據。)ui
對輸入數據進行操做,須要經過MEX函數mxGetPr 獲得數據的指針地址。 mxGetM 和 mxGetN 獲得矩陣數據的行和列 (返回整數)。對於實矩陣,咱們能夠定義 double *M; 來對實矩陣數據操做(不過彷佛是,plhs, prhs都是指向double類型的指針,因此下面的這個M等,都要定義成double*類型的)。如:spa
double *M;
int m, n;
M = mxGetPr(prhs[0]); // 指針指向第一個參數的數據地址
m = mxGetM(prhs[0]);
n = mxGetN(prhs[0]);
須要注意的是,MATLAB矩陣數據的存儲順序是"從上到下,從左到右"。也就是說對於MATLAB的m x n的矩陣A。 A(1,1) 就是 *M,A(2,1) 就是 *(M+1) ,以此類推,A(i, j) 就是 *(M + m*(j-1) + (i-1)).
注意: MATLAB的指標從1開始,C的指標從0開始。
對於輸出數據,咱們須要首先分配內存空間,有專門的mex函數可使用,如:
plhs[0] = mxCreateDoubleMatrix(m, n, mxREAL); //生成m x n 的實矩陣。
同輸入數據同樣,要對輸出數據操做,咱們也須要一個指向數據的指針變量,如
double *A;
A = mxGetPr( plhs[0]);
下面介紹一下如何使用VS2008編寫MEX並編譯調試。
打開Visual Studio 2008/2010/2012/2013, 新建項目, 選擇MFC DLL.
打開項目屬性配置頁,選擇配置屬性目錄,而後分別進行以下操做
VC++目錄 -> 包含目錄 加入MATLAB安裝目錄下的 \extern\include 路徑。
VC++目錄 -> 庫目錄 加入MATLAB的 \extern\lib\win32\microsoft 路徑。
鏈接器 -> 輸入 -> 附加依賴項 輸入libmx.lib libeng.lib libmat.lib libmex.lib
在項目源文件的. def 中EXPORTS段加入mexFunction, 如:
EXPORTS ; 此處能夠是顯式導出 mexFunction
項目文件中新建一個C++文件 如 mexproc.cpp,裏面按前面介紹的格式編寫代碼便可。
像編譯其餘程序那樣直接編譯便可,成功會生成dll文件。若是編譯連接時出錯,根據錯誤提示,檢查一下lib和h的路徑是否正確,有完好少lib文件,代碼是否有語法錯誤等。
要調試MEX程序就要先編譯,再調用它。因此咱們須要在MATLAB中調用這個函數,並在VC的MEX程序相應位置處下斷點便可。調用的函數名就是dll的主文件名,你能夠根據本身的須要更名。
咱們用mymexfun.dll爲例,先在VC的 mexFunction 函數代碼段開始處F9下斷。而後Ctrl+Alt+P附加MATLAB.exe進程。這樣就能夠運行命令調試程序了。咱們能夠在MATLAB的命令行裏輸入命令:
[輸出變量] = mymexfun(輸入變量)
程序一旦被調用,就會被斷在咱們的斷點處。接着你就能夠像調試C++程序那樣調試MEX程序了。(若是命令找不到,檢查一下matlab當前路徑,和path路徑。)
在MATLAB中編譯MEX能夠輸入: mex 文件名.cpp
MATLAB上編譯MEX時,你能夠選擇不一樣的編譯器如lc, gcc等。也能夠在編譯時附加lib和h文件。關於mex的命令詳解請參考MATLAB幫助文檔。
在使用MATLAB編譯C/C++代碼時,C/C++代碼中要使用一個mexFunction函數,那麼這個函數是如何定義,在編譯時又是如何實現的呢?下面我將使用實例進行說明。
如一個簡單的函數:
double add(double x, double y)
{
return x + y;
}
mexFunction的定義爲:
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
}
能夠看到,mexFunction是沒返回值的,它不是經過返回值把結果傳回Matlab的,而是經過對參數plhs的賦值。mexFunction的四個參數皆是說明Matlab調用MEX文件時的具體信息,如這樣調用函數時:
>> b = 1.1; c = 2.2;
>> a = add(b, c)
mexFunction四個參數的意思爲:
nlhs = 1,說明調用語句左手面(lhs-left hand side)有一個變量,即a。
nrhs = 2,說明調用語句右手面(rhs-right hand side)有兩個自變量,即b和c。
plhs是一個數組,其內容爲指針,該指針指向數據類型mxArray。由於如今左手面只有一個變量,即該數組只有一個指針,plhs[0]指向的結果會賦值給a。
prhs和plhs相似,由於右手面有兩個自變量,即該數組有兩個指針,prhs[0]指向了b,prhs[1]指向了c。要注意prhs是const的指針數組,即不能改變其指向內容。
由於Matlab最基本的單元爲array,不管是什麼類型也好,若有double array、 cell array、 struct array……因此a,b,c都是array,b = 1.1即是一個1x1的double array。而在C語言中,Matlab的array使用mxArray類型來表示。因此就不難明白爲何plhs和prhs都是指向mxArray類型的指針數組。
完整的add.c以下:
#include "mex.h"//使用mex文件必須包含頭文件 //執行具體工做的C函數 double add(double x, double y) { return x+y; } //MEX文件接口函數 void mexFunction(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[]) { double *a; double b,c; plhs[0]=mxCreateDoubleMatrix(1,1,mxREAL); a=mxGetPr(plhs[0]);//獲得第一個接收輸出變量的地址 b=*(mxGetPr(prhs[0])); c=*(mxGetPr(prhs[1])); *a=add(b,c); }
mexFunction的內容是什麼意思呢?咱們知道,若是這樣調用函數時:
>> output = add(1.1, 2.2);
在未涉及具體的計算時,output的值是未知的,是未賦值的。因此在具體的程序中,咱們創建一個1x1的實double矩陣(使用 mxCreateDoubleMatrix函數,其返回指向剛創建的mxArray的指針),而後令plhs[0]指向它。接着令指針a指向plhs [0]所指向的mxArray的第一個元素(使用mxGetPr函數,返回指向mxArray的首元素的指針)。一樣地,咱們把prhs[0]和prhs [1]所指向的元素(即1.1和2.2)取出來賦給b和c。因而咱們能夠把b和c做自變量傳給函數add,得出給果賦給指針a所指向的mxArray中的元素。由於a是指向plhs[0]所指向的mxArray的元素,因此最後做輸出時,plhs[0]所指向的mxArray賦值給output,則 output即是已計算好的結果了。
實際上mexFunction是沒有這麼簡單的,咱們要對用戶的輸入自變量的個數和類型進行測試,以確保輸入正確。如在add函數的例子中,用戶輸入char array即是一種錯誤了。
從上面的講述中咱們總結出,MEX文件實現了一種接口,把C語言中的計算結果適當地返回給Matlab罷了。當咱們已經有用C編寫的大型程序時,大可沒必要在 Matlab裏重寫,只寫個接口,作成MEX文件就成了。另外,在Matlab程序中的部份計算瓶頸(如循環),可經過MEX文件用C語言實現,以提升計算速度。
一個簡單的MEX文件例子:用m文件創建一個1000×1000的Hilbert矩陣。
% mextest.m
tic m=1000; n=1000; a=zeros(m,n); for i=1:1000 for j=1:1000 a(i,j)=1/(i+j); end end toc
在matlab中新建一個Matlab_1.cpp 文件並輸入如下程序:
#include "mex.h" //該函數是mexfunction調用的惟一一個計算子程序 void hilb(double *y,int n) { int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++) *(y+j+i*n)=1/((double)i+(double)j+1); } void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[]) { double x,*y; int n; if (nrhs!=1) mexErrMsgTxt("One inputs required."); if (nlhs != 1) mexErrMsgTxt("One output required."); if (!mxIsDouble(prhs[0])||mxGetN(prhs[0])*mxGetM(prhs[0])!=1) mexErrMsgTxt("Input must be scalars."); x=mxGetScalar(prhs[0]); plhs[0]=mxCreateDoubleMatrix(x, x, mxREAL); n=mxGetM(plhs[0]); y=mxGetPr(plhs[0]); hilb(y, n); }
該程序是一個C語言程序,它也實現了創建Hilbert矩陣的功能。在MATLAB命令窗口輸入如下命令:mex Matlab_1.cpp,便可編譯成功。進入該文件夾,會發現多了一個文件:Matlab_1.mexw32,其中Matlab_1.mexw32便是MEX文件。運行下面程序:
tic a=Matlab_1(1000); toc
由上面實驗看出,一樣功能的MEX文件比m文件快得多。
MEX文件的組成與參數
MEX文件的源代碼通常由兩部分組成:
(1)計算過程。該過程包含了MEX文件實現計算功能的代碼,是標準的C語言子程序。
(2)入口過程。該過程提供計算過程與MATLAB之間的接口,以入口函數mxFunction實現。在該過程當中,一般所作的工做是檢測輸入、輸出參數個數和類型的正確性,而後利用mx-函數獲得MATLAB傳遞過來的變量(好比矩陣的維數、向量的地址等),傳遞給計算過程。
MEX文件的計算過程和入口過程也能夠合併在一塊兒。但無論那種狀況,都要包含#include "mex.h",以保證入口點和接口過程的正確聲明。注意,入口過程的名稱必須是mexFunction,而且包含四個參數,即:
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
其中,參數nlhs和nrhs表示MATLAB在調用該MEX文件時等式左端和右端變量的個數,例如在MATLAB命令窗口中輸入如下命令:
[a,b,c]=Matlab_1(d,e,f,g)
則nlhs爲3,nrhs爲4。
MATLAB在調用MEX文件時,輸入和輸出參數保存在兩個mxArray*類型的指針數組中,分別爲prhs[]和plhs[]。prhs[0]表示第一個輸入參數,prhs[1]表示第二個輸入參數,…,以此類推。如上例中,d→prhs[0],e→prhs[1],f→prhs[2],f→prhs[3]。同時注意,這些參數的類型都是mxArray *。
接口過程要把參數傳遞給計算過程,還須要從prhs中讀出矩陣的信息,這就要用到下面的mx-函數和mex-函數。
MEX文件的源代碼通常由兩部分組成:
(1)計算過程。該過程包含了MEX文件實現計算功能的代碼,是標準的C語言子程序。
(2)入口過程。該過程提供計算過程與MATLAB之間的接口,以入口函數mxFunction實現。在該過程當中,一般所作的工做是檢測輸入、輸出參數個數和類型的正確性,而後利用mx-函數獲得MATLAB傳遞過來的變量(好比矩陣的維數、向量的地址等),傳遞給計算過程。
MEX文件的計算過程和入口過程也能夠合併在一塊兒。但無論那種狀況,都要包含#include "mex.h",以保證入口點和接口過程的正確聲明。注意,入口過程的名稱必須是mexFunction,而且包含四個參數,即:
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
其中,參數nlhs和nrhs表示MATLAB在調用該MEX文件時等式左端和右端變量的個數,例如在MATLAB命令窗口中輸入如下命令:
[a,b,c]=Matlab_1(d,e,f,g)
則nlhs爲3,nrhs爲4。
MATLAB在調用MEX文件時,輸入和輸出參數保存在兩個mxArray*類型的指針數組中,分別爲prhs[]和plhs[]。prhs[0]表示第一個輸入參數,prhs[1]表示第二個輸入參數,…,以此類推。如上例中,d→prhs[0],e→prhs[1],f→prhs[2],f→prhs[3]。同時注意,這些參數的類型都是mxArray *。
接口過程要把參數傳遞給計算過程,還須要從prhs中讀出矩陣的信息,這就要用到下面的mx-函數和mex-函數。
在MATLAB6.5版本中,提供的mx-函數有106個,mex-函數有38個,下面咱們僅介紹經常使用的函數。
該函數是C MEX文件的入口函數,它的格式是固定的:
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
說明:MATLAB函數的調用方式通常爲:[a,b,c,…]=被調用函數名稱(d,e,f,…),nlhs保存了等號左端輸出參數的個數,指針數組plhs具體保存了等號左端各參數的地址,注意在plhs各元素針向的mxArray內存未分配,需在接口過程當中分配內存;prhs保存了等號右端輸入參數的個數,指針數組prhs具體保存了等號右端各參數的地址,注意MATLAB在調用該MEX文件時,各輸入參數已存在,因此在接口過程當中不須要再爲這些參數分配內存。
兩函數的具體格式以下:
#include "mex.h"
void mexErrMsgTxt(const char *error_msg);
void mexWarnMsgTxt(const char *warning_msg);
其中error_msg包含了要顯示錯誤信息,warning_msg包含要顯示的警告信息。兩函數的區別在於mexErrMsgTxt顯示出錯信息後即返回到MATLAB,而mexWarnMsgTxt顯示警告信息後繼續執行。
兩函數具體格式以下:
#include "mex.h"
int mexCallMATLAB(int nlhs, mxArray *plhs[],
int nrhs, mxArray *prhs[], const char *command_name);
int mexEvalString(const char *command);
mexCallMATLAB前四個參數的含義與mexFunction的參數相同,command_name能夠MATLAB內建函數名、用戶自定義函數、M文件或MEX文件名構成的字符串,也能夠MATLAB合法的運算符。
mexEvalString用來操做MATLAB空間已存在的變量,它不返回任何參數。
mexCallMATLAB與mexEvalString差別較大,請看下面的例子。
【例2】試用MEX文件求5階徹底圖鄰接矩陣 的特徵值及對應的特徵向量。
5階徹底圖的鄰接矩陣爲:(這裏找不到圖片了,抱歉。不過不會影響您對本文的理解。)
下面是求該矩陣的MEX文件。
Matlab_2.cpp
#include "mex.h" void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[]) { double x; mxArray *y,*z,*w; int n; if (nrhs!=1) mexErrMsgTxt("One inputs required."); if (nlhs != 3) mexErrMsgTxt("Three output required."); if (!mxIsDouble(prhs[0])||mxGetN(prhs[0])*mxGetM(prhs[0])!=1) mexErrMsgTxt("Input must be a scalar."); x=mxGetScalar(prhs[0]); plhs[0]=mxCreateDoubleMatrix(x,x,mxREAL); plhs[1]=mxCreateDoubleMatrix(x,x,mxREAL); plhs[2]=mxCreateDoubleMatrix(x,x,mxREAL); n=mxGetM(plhs[0]); y=plhs[0]; z=plhs[1]; w=plhs[2]; //利用mexCallMATLAB計算特徵值 mexCallMATLAB(1,&plhs[1],1,prhs,"ones"); mexCallMATLAB(1,&plhs[2],1,prhs,"eye"); mexCallMATLAB(1,&plhs[0],2,&plhs[1],"-"); mexCallMATLAB(2,&plhs[1],1,&plhs[0],"eig"); //演示mexEvalString的功能 mexEvalString("y=y*2"); mexEvalString("a=a*2"); }
在MATLAB命令窗口輸入如下命令:
>> mex Matlab_2.cpp >> clear >> a=magic(5) a = 17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 9 >> [y,z,w]=Matlab_2(5) ??? Undefined function or variable 'y'. a = 34 48 2 16 30 46 10 14 28 32 8 12 26 40 44 20 24 38 42 6 22 36 50 4 18 y = 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 z = 0.8333 -0.1667 -0.1667 0.2236 0.4472 -0.1667 0.8333 -0.1667 0.2236 0.4472 -0.1667 -0.1667 0.8333 0.2236 0.4472 -0.5000 -0.5000 -0.5000 0.2236 0.4472 0 0 0 -0.8944 0.4472 w = -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 4
由上面能夠看出,K5的特徵值爲–1和4,其中–1是四重根。MATLAB提供了mexGetVariable、mexPutVariable函數,以實現MEX空間與其它空間交換數據的任務,具體能夠參看MATLAB幫助文檔。
其格式具體以下:
#include "matrix.h"
mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag);
其中m表明行數,n表明列數,ComplexFlag可取值mxREAL 或mxCOMPLEX。若是建立的矩陣須要虛部,選擇mxCOMPLEX,不然選用mxREAL。
相似的函數有:
mxCreateCellArray |
建立n維元胞mxArray |
mxCreateCellMatrix |
建立二維元胞mxArray |
mxCreateCharArray |
建立n維字符串mxArray |
mxCreateCharMatrixFromStrings |
建立二維字符串mxArray |
mxCreateDoubleMatrix |
建立二維雙精度浮點mxArray |
mxCreateDoubleScalar |
建立指定值的二維精度浮點mxArray |
mxCreateLogicalArray |
建立n維邏輯mxArray,初值爲false |
mxCreateLogicalMatrix |
建立二維邏輯mxArray,初值爲false |
mxCreateLogicalScalar |
建立指定值的二維邏輯mxArray |
mxCreateNumericArray |
建立n維數值mxArray |
mxCreateNumericMatrix |
建立二維數值mxArray,初值爲0 |
mxCreateScalarDouble |
建立指定值的雙精度mxArray |
MxCreateSparse |
建立二維稀疏mxArray |
mxCreateSparseLogicalMatrix |
建立二維稀疏邏輯mxArray |
MxCreateString |
建立指定字符串的1 n的串mxArray |
mxCreateStructArray |
建立n維架構mxArray |
mxCreateStructMatrix |
建立二維架構mxArray |
其格式以下:
#include "matrix.h"
int mxGetM(const mxArray *array_ptr);
int mxGetN(const mxArray *array_ptr);
與之相關的還有:
mxSetM:設置矩陣的行維
mxSetN:設置矩陣的列維
其格式以下:
#include "matrix.h"
double *mxGetPr(const mxArray *array_ptr);
double *mxGetPi(const mxArray *array_ptr);
與之相關的函數還有:
mxSetPr:設置矩陣的實部
mxSetPi:設置矩陣的虛部
【例3】實現字符串的倒序輸出。
#include "mex.h" void revord(char *input_buf,int buflen,char *output_buf) { int i; //實現字符串倒序 for(i=0;i<buflen-1;i++) *(output_buf+i)=*(input_buf+buflen-i-2); } void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[]) { //定義輸入和輸出參量的指針 char *input_buf,*output_buf; int buflen,status; //檢查輸入參數個數 if(nrhs!=1) mexErrMsgTxt("One input required."); else if(nlhs>1) mexErrMsgTxt("Too many output arguments.");
//檢查輸入參數是不是一個字符串 if(mxIsChar(prhs[0])!=1) mexErrMsgTxt("Input must be a string.");
//檢查輸入參數是不是一個行變量 if(mxGetM(prhs[0])!=1) mexErrMsgTxt("Input must a row vector.");
//獲得輸入字符串的長度 buflen=(mxGetM(prhs[0])*mxGetN(prhs[0]))+1; //爲輸入和輸出字符串分配內存 input_buf=mxCalloc(buflen,sizeof(char)); output_buf=mxCalloc(buflen,sizeof(char)); //將輸入參量的mxArray結構中的數值拷貝到C類型字符串指針 status=mxGetString(prhs[0],input_buf,buflen); if(status!=0) mexWarnMsgTxt("Not enough space. String is truncated."); //調用C程序 revord(input_buf,buflen,output_buf); plhs[0]=mxCreateString(output_buf); }
這個程序中須要注意的地方是mxCalloc函數,它代替了標準C程序中的calloc函數用於動態分配內存,而mxCalloc函數採用的是MATLAB的內存管理機制,並將全部申請的內存初始化爲0,所以凡是C代碼須要使用calloc函數的地方,對應的Mex文件應該使用mxCalloc函數。一樣,凡是C代碼須要使用realloc函數的地方,對應的Mex文件應該使用mxRealloc函數。
在MATLAB命令窗口中對revord.cpp程序代碼編譯連接:
>> mex revord.cpp
在MATLAB命令窗口中對C-MEX文件revord.dll進行測試:
>> x='I am student.';
>> revord(x)
ans =
.tneduts ma I
[原做者贈言] 終於寫完了,相信你們對mex文件應該有點熟悉了,具體還要到實際應用中慢慢體會。