matlab 與c/c++ 混合MEX的編程

     matlab中矩陣預算特別方便,但若是有沒法避免的循環甚至多層嵌套的話,會很是影響程序的效率,所以一般會將這種須要大量循環的模塊用c++編寫而後在matlab中調用。matlab中的的c++編程稱爲mex編程(matlab executive),其中須要些一個c++文件,而後在matlab中用mex命令編譯它,而後就能夠在matlab中像調用函數同樣調用c++代碼了。 c++

     要使用mex編譯,首先要在matlab中配置c++編譯器,若是你的計算機中已經安裝了某個c++編譯器(例如vs或者gcc),在matlab命令行中直接輸入"mex -setup"就會有相應的提示,而後選擇下一步肯定,編譯器就設置成功了。編程

     接着就能夠開始編寫mex的c++文件了,下面舉個例子,新建一個名爲test.cpp的c++文件,代碼內容以下:數組

#include "mex.h"
void mexFunction(int nlhs,mxArray* plhs[],int nrhs,mxArray* rhs[])
{
	printf("hello world!");
}

  mex的源文件必定要include頭文件"mex.h「,mex源文件的入口函數爲void mexFunction,這個函數有四個參數:nlhs(n left hand side) 等號左邊參數個數,plhs(pointer left hand side)等號左邊參數指針,另外兩個參數是等號右邊的個數和指針。ide

     由於mex編譯完成以後在matlab中是當函數用的,函數就有輸入參數和返回參數,輸入參數就是等號右邊的參數,輸出參數就是等號左邊的參數,matlab中的函數容許有多個返回值,因此nlhs能夠大於一。注意,指針plhs和prhs的類型爲mxArray*,這是mex.h中定義的一種數據類型,是matlab裏矩陣在c++源文件中的表示。函數

     當編寫完上面的源文件以後,在matlab裏執行命令"mex 文件名" 就能夠編譯這個文件,在這裏用 mex  test.cpp編譯它,編譯完成以後能夠看到同目錄下生成了一個同名的.mexw32(或者.mexw64,取決於你的編譯器是32bit仍是64bit)文件,而後在matlab命令中輸入test(),就能夠看到打印出了"hello world "。這裏想再次強調,mex源文件編譯完成以後在matlab裏當函數調用的。ui

   

1) mex編程中指針和索引:編碼

      matlab中默認的數據類型是double,用class()函數能夠看到變量的數據類型:spa

                                    

    分析下面一段代碼,matlab代碼以下:命令行

mex test.cpp '-g';
a = [1.1,2.1,3;4,5,6;7,8,9]
mex(a)

      命令mex 用來編譯mex文件,上面代碼中  mex  test.cpp ‘-g’ 編譯了test.cpp這個c++文件,編譯完成以後會生成一個「test.mexw64」的文件,後綴名說明這是在win64下編譯完成的mex文件,後面的'-g'是一個附加參數,在這裏不用理解。編譯後的mex文件能夠當matlab函數使用。    指針

      在matlab代碼文件同目錄下的c文件test.cpp代碼以下:

#include 'mex.h'
void
mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { double *input; input = mxGetPr(prhs[0]); printf("第一個值%f\n",*input); printf("第二個值%f\n",*(input+1)); }

運行.m文件,打印出結果以下:

     

      在mex.cpp的代碼中,mexFunction有四個參數,nlhs( number left hand s):左邊參數個數,也就是matlab函數輸出值得個數,mxArray *plhs[]是一個指針數組,數組中的每個元素都是一個指針,指向輸出的矩陣;nrhs 是右邊參數個數,也就是輸入參數的個數,mxArray *prhs[]數組中的每一個指針指向輸入矩陣。mxGetPr()函數返回一個double*型的指針,指向矩陣的第一個元素,在matlab代碼中調用:mex(a),那也就是 prhs[0]是輸入矩陣a的地址,而 input = mxGetPr(prhs[0]) ,input指向了a第一個元素1 。
     那矩陣第一排第二列的值a(1,2)的地址是多少呢?是(input+1)嗎?在這裏咱們運行上面的matlab代碼,

     能夠看出,輸出的*(input+1)是4,也就是說,c++中的matlab矩陣是按列進行索引的。這裏是一個須要注意的地方,由於不少地方要對matlab輸入的矩陣進行遍歷獲得矩陣的元素值,若是索引出錯,那就徹底錯了。其實這裏的內在緣由,是由於在matlab中矩陣是按列進行索引的,而c++中指針式按行日後加的。

     有不少函數能夠方便咱們對矩陣進行索引,uint32 mxGetM(mxArray *)輸入一個矩陣的指針,返回該矩陣的行數,uint32 mxGetN(mxArray *)返回列數,對行數和列數適當的計算,能夠方便的訪問矩陣元素,例如,訪問a(i,j):   *(input+N*(j-1)+(i-1))  ,N爲矩陣行數,這裏須要-1的緣由是,matlab的行數列數從1開始計數,而c的數組則從0開始索引。

 

2)mex編程中的數據類型與指針移位的重要關係,mxGetPr() 與 mxGetData():

     前面說過,matlab裏的默認數據類型是double,那麼,若是把mex函數的輸入矩陣的數據類型轉換一下,會出現什麼結果呢?

matlab 代碼:

1 clc
2 mex mex.cpp '-g';
3 a = [1.1,2.1,3;4,5,6;7,8,9];
4 a=single(a)
5 mex(a)

c++代碼:

1 #include "mex.h"
2 void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
3 {
4     double *input;
5     input = mxGetPr(prhs[0]);
6     printf("第一個值%f\n",*input);
7     printf("第二個值%f\n",*(input+1));
8 }

c++代碼並無變,matlab代碼也僅僅進行了一個數據類型轉換,咱們看看輸出結果:

      能夠看到這裏輸出的已經不是咱們指望的數值了。在我調試mex代碼的時候這個問題苦惱了我好久,由於mex不方便調試,不少時候輸出的結果不是想要的,並且個人輸入矩陣都是上萬維的,很難調試。這裏輸入矩陣a變成了single單精度類型,前面咱們說過,mxGetPr()返回double類型的指針,當咱們用double類型指針訪問一個單精度(在c++)中咱們稱之爲浮點型float的數據的時候,固然會發成內存越界,用取值符號*去取值的時候超過了數據的內存塊,所以發生錯誤,若是咱們修改c++代碼:

1 #include "mex.h"
2 void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
3 {
4     float* input;
5     input = (float*)mxGetPr(prhs[0]);
6     printf("第一個值%f\n",*input);
7     printf("第二個值%f\n",*(input+1));
8 }

     將input類型設置成float,並將mxGetPr()的返回類型強制轉換爲float*就能夠了。在這裏還有一個函數mxGetData()也能夠返回輸入矩陣的頭地址,只不過mxGetData()返回的是char*類型的指針,而mxGetPr()返回的是double*類型的指針,能夠根據本身的須要選取函數,或者轉換指針類型。若是指針類型不對,極有可能形成內存訪問錯誤,致使matlab死掉。

 

3) nlhs 與 nrhs的做用 

     mexFunction函數中,兩個指針參數分別指向輸入輸出的矩陣,而nrhs和nlhs分別記錄輸入輸出矩陣的個數,在通常的操做中,咱們僅僅對輸入矩陣進行取值,運算,對輸出矩陣進行賦值,nrhs和nlhs不是很經常使用,可是也是極其重要的。例如,在上面的代碼中,若是我在matlab代碼中這樣調用mex:mex(),不輸入任何參數,matlab就會立刻死掉。由於在mex文件的cpp代碼中,你用指針訪問了輸入矩陣的值,而在參數中你沒有給mex輸入任何參數,使得矩陣指針爲野指針,致使內存錯誤。若是編碼中出現這種參數不對的狀況,將致使matlab頻繁死掉,個人工做中數據特別多,準備數據須要幾十分鐘,這樣讓我很是痛苦。解決的方法就是利用nlhs和nrhs這兩個參數。在mexFunction中判斷nlhs的值來判斷輸入參數的個數,用nrhs判斷輸入參數的個數。若是輸入參數少於某個值或者不知足你的要求可讓mexFunction直接return,避免後續的程序致使內存錯誤。

相關文章
相關標籤/搜索