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


     matlab中矩陣預算特別方便,但如果有無法避免的循環甚至多層嵌套的話,會非常影響程序的效率,因此通常會將這種需要大量循環的模塊用c++編寫然后在matlab中調用。matlab中的的c++編程稱為mex編程(matlab executive),其中需要些一個c++文件,然后在matlab中用mex命令編譯它,然后就可以在matlab中像調用函數一樣調用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)等號左邊參數指針,另外兩個參數是等號右邊的個數和指針。

     因為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里當函數調用的。

   

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

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

                                    

    分析下面一段代碼,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,避免后續的程序導致內存錯誤。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM