Matlab调用C语言mexFunction入口函数

文章目录

    • 一、什么是MEX-file
    • 二、如何创建可供Matlab直接调用的MEX-file
        • 1.安装Matlab支持的C/C++编译器
        • 2.创建.c/.cpp文件
    • 三、输入数据操作
        • 1、传入整数类型数据
        • 2、传入double类型数据
        • 3、传入矩阵类型数据
    • 四 、输出数据操作
  • 参考资料

  Matlab与C/C++混合编程有很多种方式,分别适用于不同的情况。

  • 程序主体用Matlab编写,有一些特别耗时的函数用C/C++改写来提高效率,或者已经有现成的C/C++函数,应用到Matlab程序中(本文属于这种情况)
  • 程序主体用C/C++编写,部分程序想调用Matlab函数减少开发时间,本文不涉及这种情况,建议读者自行查阅Matlab帮助文档

  Matlab 可以调用C编译器编译的C程序,通过MEX文件的调用,其调用方式与MATALAB的内建函数完全相同,只需要在命令窗口内输入对应的文件名称即可。

一、什么是MEX-file

  简单来说MEX-file是一种预编译的,用其他语言(C/C++,Fortran)编写的函数库,可以直接被Matlab调用。

  正如前面提到的,这种方式适用于两种情况:

  • 程序中有一部分代码耗时巨大,想通过改写这部分函数提高速度
  • 已经有大量C/C++或Fortran的函数库,想直接用Matlab调用,避免重复开发

  这两种情况用MEX-file的这种方案来解决都是非常合适的,因为这种调用方式非常方便,你需要注意地只是数据结构的转换。这种方式支持C/C++和Fortran,本文主要讲C/C++。

  C语言MEX程序代码文件有 计算子例程(Computational routine)和接口子程序(GatWay routine)两个相互独立的子程序组成。其中,计算子例程 的功能是完成所需要的计算,它和具有相同功能的一般C源程序文件相同;接口子程序 的功能则是计算子程序和MATALAB的接口,用户实现两个不同内存空间中的通信。

二、如何创建可供Matlab直接调用的MEX-file

1.安装Matlab支持的C/C++编译器

2.创建.c/.cpp文件

  这一步可以用Matlab的编辑器也可以用其他你喜欢的编辑器,需要注意的是: 将来在Matlab中调用的函数名即为此处你创建的文件名,而不是文件内的函数名

  MEX-file的内容,一个完整的MEX-file应该包括:

  • #include MEX-file头文件
  • mexFunction 入口函数(C/C++中的main函数)
  • 输入输出的数据的校验
  • 变量的传递
  • 你自己编写的功能函数
  • mexFunction 入口函数
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
nlhs:函数左侧,输出参数数目   (Left-hand side)
plhs:函数左侧,指向输出参数的指针 
nrhs:函数右侧,输入参数数目
prhs:函数右侧,指向输入参数的指针

例如,使用
[a,b]=test(c,d,e)

  • 当调用mex函数test时,传给test的这三个参数分别是: prhs[0]=c ,prhs[1]=d ,prhs[2]=e
  • 当函数返回时,将会把你放在 plhs[0],plhs[1]里的地址赋给a和b,达到返回数据的目的。

  细心的你也许已经注意到,prhs[i] 和 plhs[i]都是指向类型mxArray类型数据的指针。 这个类型是在mex.h中定义的,事实上,在Matlab里大多数数据都是以这种类型存在。当然还有其他的数据类型,可以参考Api guide.pdf里的介绍。

三、输入数据操作

1、传入整数类型数据

  为了让大家能更直观地了解参数传递的过程,我们把hello.c改写一下,使它能根据输 入参数的变化给出不同的屏幕输出:

//hello.cpp 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"); 
}

  在matlab命令窗口中输入: mex hello.cpp 进行编译后,然后在输入命令:hello(1) 和 hello(0) 会得到如下图所示的结果。
Matlab调用C语言mexFunction入口函数_第1张图片

  程序hello已经可以根据输入参数来给出相应的屏幕输出。在这个程序里,除了用到了屏幕输出函数mexPrintf(用法跟c里的printf函数几乎完全一样)外,还用到了一个函数mxGetScalar,调用方式如下:

  i=mxGetScalar(prhs[0]); 

  "Scalar"就是 标量 的意思。在Matlab里数据都是以数组的形式存在的,mxGetScalar的作用就是把通过prhs[0]传递进来的mxArray类型的指针指向的数据(标量)赋给C程序里的变量。这个变量本来应该是double类型的,通过强制类型转换赋给了整型变量 i i i。既然有标量,显然还应该有矢量,否则矩阵就没法传了。看下面的程序:

2、传入double类型数据

#include "mex.h"   
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])   
{  
    double *i;   
    i=mxGetPr(prhs[0]); //get input parameter  
    if(i[0]==1)   
      mexPrintf("hello,world!\n");   
    else   
      mexPrintf("大家好!!!!\n");   
} 

  在上面的程序中,通过mxGetPr函数从指向 mxArray 类型数据的 prhs[0] 获得了指向 double类型 的指针。

3、传入矩阵类型数据

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

#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");
        }
    }
}

Matlab调用C语言mexFunction入口函数_第2张图片
Matlab调用C语言mexFunction入口函数_第3张图片
  对输入数据进行操作,需要通过MEX函数mxGetPr 得到数据的指针地址。 mxGetM 和 mxGetN 得到矩阵数据的行和列 (返回整数)。对于实矩阵,我们可以定义double *data 来对实矩阵数据操作(不过似乎是,plhs, prhs都是指向double类型的指针,所以data定义成double*类型的)

  需要注意的是,MATLAB矩阵数据的存储顺序是"从上到下,从左到右",(即按列存取)。另外,MATLAB的指标从1开始,C的指标从0开始,也就是说对于 MATLAB 的 m × n m \times n m×n 的矩阵 A。 A(1,1) 就是 *data,A(2,1) 就是 *(data+1) ,以此类推,A(i, j) 就是 (data+ m(j-1) + (i-1)).

四 、输出数据操作

  输入数据是在函数调用之前已经在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的基础上稍作改变得到的,功能是将输入的矩阵进行逆操作。

#include "mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    double *inData;
    double *outData;
    int M,N;
    int i,j;
    if(nrhs!=1)   //输入参数的个数为0
        mexErrMsgTxt("USAGE: b=reverse(a)\n");
    if(!mxIsDouble(prhs[0]))   //输入的参数不是double型
    {
        mexErrMsgTxt("the Input Matrix must be double!\n");  
    }
    else
    {
        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[i*M+j];      // 行列转置
                outData[j*M+i] = inData[(N-1-j)*M+i];   // 列进行了逆序;第一列变成最后一列
            }
        }
    }      
}

执行列的逆序排列:outData[j*M+i] = inData[(N-1-j)*M+i]
Matlab调用C语言mexFunction入口函数_第4张图片
执行行列转置:outData[j*M+i] = inData[i*M+j];
Matlab调用C语言mexFunction入口函数_第5张图片

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

  需要说明的是,Matlab提供的API中,函数前缀有mex-mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mex前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mexErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。
至此为止,使用C编写mex函数的基本过程已经介绍完了。

参考资料

  • MATLAB中mexFunction函数的接口规范https://blog.csdn.net/yshshhgxq/article/details/91351535
  • Matlab与C/C++混合编程 Matlab调用C函数https://www.cnblogs.com/ynnie/p/9239347.html

你可能感兴趣的:(Matlab)