Matlab与VC混合MEX编程之关于mexFunction篇

转自:1. http://blog.sina.com.cn/s/blog_57e47bf60100031o.html 

           2. http://www.cnblogs.com/rong86/p/3703704.html

与C中的main函数一样,MEX程序中的开始函数为mexFunction.默认变量参数是:
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
其中
nlhs指的是在调用函数时返回值的个数;
plhs[]是每个返回值(在MATLAB中都是矩阵)的指针;
nrhs指的是调用函数时的参数个数;
prhs[]是每个参数的指针。
 
在函数内部调用时,常用函数用法如下:
 
mxIsNumeric(prhs[0]); // 判断变量是否是数字
mxIsDouble(prhs[0]);   // 判断变量是否为双精度
mxIsEmpty(prhs[0]);     // 判断变量是否为空值
mxIsComplex(prhs[0]); // 判断变量是否为复数
mexPrintf("%d", y);     // 命令行中打印数据
 
         = mxGetN(prhs[0]);                     // 获取各输入参数的长度
         = mxGetM(prhs[1]);                     // 获取输入参数的列数
points = mxGetScalar(prhs[1]); // 将返回变量的实部,为双精度形数据
plhs[0]= mxCreateDoubleMatrix(1, 1, mxREAL); // 创建1×1双精度返回变量
 
         = mxGetData(prhs[0]); // 获取变量的指针,功能相当于mxGetPr()
         = mxGetPr(plhs[0]);     // 获取变量的指针,数据为实数
 对mexFunction的参数是进行指针操作的,不能用单纯的return返回值。MEX程序传送回来的整数数据要变为双精度型数据,才能为其它函数所处理。


例如:使用[a,b]=test(c,d,e)
 调用mex函数test时,传给test的这三个参数分别是prhs[0]=c ,prhs[1]=d ,prhs[2]=e
prhs[i]和plhs[i]都是指向类型mxArray类型数据的指针。 这个类型是在mex.h中定义的,事实上,在Matlab里大多数数据都是以这种类型存在。当然还有其他的数据类型,可以参考Apiguide.pdf里的介绍。 
把hello.c改写一下,使它能根据输入参数的变化给出不同的屏幕输出:
复制代码
//hello.c 2.0 
#include "mex.h" 
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 
{
int i; 
i=mxGetScalar(prhs[0]); 
if(i==1) 
mexPrintf("hello,world!\n"); 
else 
mexPrintf("大家好!\n"); 
}
复制代码
将这个程序编译通过后,执行hello(1),屏幕上会打出: 
hello,world! 
而hello(0)将会得到: 
大家好! 
现在,程序hello已经可以根据输入参数来给出相应的屏幕输出。在这个程序里,除了用到了屏幕输出函数mexPrintf(用法跟c里的printf函数几乎完全一样)外,还用到了一个函数:mxGetScalar,调用方式如下: 
i=mxGetScalar(prhs[0]); 
"Scalar"就是标量的意思。在Matlab里数据都是以数组的形式存在的,mxGetScalar的作用就是把通过prhs[0]传递进来的mxArray类型的指针指向的数据(标量)赋给C程序里的变量。这个变量本来应该是double类型的,通过强制类型转换赋给了整形变量i。既然有标量,显然还应该有矢量,否则矩阵就没法传了。看下面的程序: 
复制代码
//hello.c 2.1 
#include "mex.h" 
void mexFunction(int nlhs, mxArray *plhs[], 
int nrhs, const mxArray *prhs[]) 
{ 
int *i; 
i=mxGetPr(prhs[0]); 
if(i[0]==1) 
  mexPrintf("hello,world!\n"); 
else 
  mexPrintf("大家好!\n"); 
}  
复制代码

这样,就通过mxGetPr函数从指向mxArray类型数据的prhs[0]获得了指向double类型的指针
但是,还有个问题,如果输入的不是单个的数据,而是向量或矩阵,那该怎么处理呢 ?通过mxGetPr只能得到指向这个矩阵的指针,如果我们不知道这个矩阵的确切大小,就 
没法对它进行计算。 
为了解决这个问题,Matlab提供了两个函数mxGetM和mxGetN来获得传进来参数的行数 和列数。下面例程的功能很简单,就是获得输入的矩阵,把它在屏幕上显示出来: 

复制代码
//show.c 1.0 
#include "mex.h" 
#include "mex.h" 
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 
{ 
double *data; 
int M,N; 
int i,j; 
data=mxGetPr(prhs[0]); //获得指向矩阵的指针 
M=mxGetM(prhs[0]); //获得矩阵的行数 
N=mxGetN(prhs[0]); //获得矩阵的列数 
for(i=0;i<M;i++) 
{   for(j=0;j<N;j++) 
     mexPrintf("%4.3f  ",data[j*M+i]); 
     mexPrintf("\n"); 
  }
} 
复制代码

编译完成之后,用下面的命令测试一下:

 a=1:10; 
  b=[a;a+1]; 
  show(a) 
  show(b) 

需要注意的是,在Matlab里,矩阵第一行是从1开始的,而在C语言中,第一行的序数为0,Matlab里的矩阵元素b(i,j)在传递到C中的一维数组大data后对应于data[j*M+i]。

输入数据是在函数调用之前已经在Matlab里申请了内存的,由于mex函数与Matlab共用同一个地址空间,因而在prhs[]里传递指针就可以达到参数传递的目的。但是输出参数却需要在mex函数内申请到内存空间才能将指针放在plhs[]中传递出去。由于返回指针类型必须是mxArray,所以Matlab专门提供了一个函数mxCreateDoubleMatrix来实现内存的申请,函数原型如下: 

mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag) 
m:待申请矩阵的行数 
n:待申请矩阵的列数 
为矩阵申请内存后,得到的是mxArray类型的指针,就可以放在plhs[]里传递回去了。但是对这个新矩阵的处理,却要在函数内完成,这时就需要用到前面介绍的mxGetPr。使用mxGetPr获得指向这个矩阵中数据区的指针(double类型)后,就可以对这个矩阵进行各种操作和运算了。下面的程序是在上面的show.c的基础上稍作改变得到的,功能是将输入矩阵进行上下翻转
复制代码
//reverse.c 1.0 
#include "mex.h" 
void mexFunction(int nlhs, mxArray *plhs[], 
    int nrhs, const mxArray *prhs[]) 
{ 
double *inData; 
double *outData; 
int M,N; 
int i,j; 
inData=mxGetPr(prhs[0]); 
M=mxGetM(prhs[0]); 
N=mxGetN(prhs[0]); 
plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL); 
outData=mxGetPr(plhs[0]); 
for(i=0;i<M;i++) 
  for(j=0;j<N;j++) 
   outData[j*M+i]=inData[(N-1-j)*M+i]; 
} 
复制代码

当然,Matlab里使用到的并不是只有double类型这一种矩阵,还有字符串类型、稀疏矩阵、结构类型矩阵等等,并提供了相应的处理函数。本文用到编制mex程序中最经常遇到的一些函数,其余的详细情况清参考Apiref.pdf。

通过前面两部分的介绍,大家对参数的输入和输出方法应该有了基本的了解。具备了这些知识,就能够满足一般的编程需要了。但这些程序还有些小的缺陷,以前面介绍的re由于前面的例程中没有对输入、输出参数的数目及类型进行检查,导致程序的容错性很差,以下程序则容错性较好
复制代码
#include "mex.h" 
void mexFunction(int nlhs, mxArray *plhs[],  int nrhs, const mxArray *prhs[]) 
{ 
double *inData; 
double *outData; 
int M,N; 
//异常处理 
//异常处理 
if(nrhs!=1) 
    mexErrMsgTxt("USAGE: b=reverse(a)\n"); 
  if(!mxIsDouble(prhs[0])) 
   mexErrMsgTxt("the Input Matrix must be double!\n"); 
   inData=mxGetPr(prhs[0]); 
   M=mxGetM(prhs[0]); 
   N=mxGetN(prhs[0]); 
   plhs[0]=mxCreateDoubleMatrix(M,N,mxREAL); 
   outData=mxGetPr(plhs[0]); 
   for(i=0;i<M;i++) 
     for(j=0;j<N;j++) 
     outData[j*M+i]=inData[(N-1-j)*M+i]; //总之,在C中,二维数组也是按照一维处理的
  } 
复制代码

在上面的异常处理中,使用了两个新的函数:mexErrMsgTxt和mxIsDouble。MexErrMsgTxt在给出出错提示的同时退出当前程序的运行MxIsDouble则用于判断mxArray中的数据是否double类型。当然Matlab还提供了许多用于判断其他数据类型的函数,这里不加详述。 
需要说明的是,Matlab提供的API中,函数前缀有mex-和mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mex前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mxErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。
至此为止,使用C编写mex函数的基本过程已经介绍完了。 

在函数内部调用时,常用函数用法如下:
 
复制代码
mxIsNumeric(prhs[0]); // 判断变量是否是数字
mxIsDouble(prhs[0]); // 判断变量是否为双精度
mxIsEmpty(prhs[0]); // 判断变量是否为空值
mxIsComplex(prhs[0]); // 判断变量是否为复数
mexPrintf("%d", y); // 命令行中打印数据
 
x = mxGetN(prhs[0]); // 获取输入参数的行数  
y = mxGetM(prhs[1]); // 获取输入参数的列数
points = mxGetScalar(prhs[1]); // 将返回变量的实部,为双精度形数据
plhs[0]= mxCreateDoubleMatrix(1, 1, mxREAL); // 创建1×1双精度返回变量
 
x = mxGetData(prhs[0]); // 获取变量的指针,功能相当于mxGetPr()
y = mxGetPr(plhs[0]); // 获取变量的指针,数据为实数 
复制代码
对mexFunction的参数是进行指针操作的,不能用单纯的return返回值。MEX程序传送回来的整数数据要变为双精度型数据,才能为其它函数所处理。
注意:mxarray stores data in one dimension , even its a matrix 
if your c program is preparing a matrix for matlab, it need to be reshaped into one D
if your c program is getting data out of matlab, you need to convert yourself to become a meaningful data 
 
matlab的数组在C中都按照一维存储的但是这并不影响mxGetM,mxGetN,mxGetDimensions得到数据在matlab中的维度,如果matlab中输入的数据是4行5列的数据,mxGetM得到的就是4,mxGetN得到的就是5
但是在运算时必须按照1维的来运算,例如还是假设你输入的是4行5列的矩阵,你这里得到
int mBB = mxGetM(prhs[0]); // 行数mBB为4
int nBB = mxGetN(prhs[0]); // 列数nBB为5
double *bb = mxGetPr(prhs[0]); // 得到矩阵中数据的指针
这个时候如果你想调用原矩阵中3行2列的元素,即matlab中的bb(3,2),也即bb(4*(2-1)+3)也就是bb(7);那么在C中应该对应的是bb[mBB*(2-1)+(3-1)]也就是b[7-1],也就是bb[6](从这可以看出,是按照列数的)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值