一、为什么在Matlab中使用C
- 性能优化:C语言提供了接近硬件的编程能力,使得开发者可以编写高效率的代码。对于计算密集型任务,尤其是那些需要大量数值计算的场景,用C语言编写的函数通常比MATLAB内置函数运行得更快。这是因为C语言允许更细致地控制内存使用和处理器指令。
- 现有代码库的利用:在某些领域,已有大量用C或C++编写的成熟、高效的代码库。通过MEX接口,MATLAB可以直接调用这些代码库,无需重写这些功能。这样不仅可以节省开发时间,还能确保使用经过验证的、高质量的代码。
- 扩展MATLAB功能:虽然MATLAB提供了广泛的函数库,但在某些特定应用领域,用户可能需要一些MATLAB本身不提供的特殊功能。通过使用C语言,开发者可以创建自定义的MEX文件来扩展MATLAB的功能,满足特定的需求。
- 硬件接口:在需要与硬件设备(如传感器、仪器或其他外部设备)直接交互时,C语言提供了访问底层硬件接口的能力,例如通过串口或网络接口。通过在MATLAB中集成C语言编写的代码,用户可以实现对这些设备的控制和数据采集。
- 跨平台兼容性:C语言具有很好的跨平台兼容性,编写的代码可以在不同操作系统上编译运行,这对于需要在多个平台上部署MATLAB应用的场景非常有用。
- 专业算法实现:对于一些高度专业化的算法,可能只有少数专家知道如何高效实现。这些专家可能习惯于使用C语言,因此他们编写的高效算法往往以C语言的形式存在。将这些算法通过MEX接口集成到MATLAB中,可以让更广泛的用户受益。
二、MEX重要接口概览
mex.h
是MATLAB提供的一个头文件,专为MEX文件开发而设计。它包含了一系列的宏定义、类型定义和函数原型,这些都是在C或C++代码中与MATLAB数据进行交互所必需的。通过这些接口,开发者可以创建MEX文件,这些文件能够直接从MATLAB环境中被调用
以下是mex.h
中提供的一些重要接口的概览:
-
内存管理:
mxMalloc
、mxCalloc
、mxRealloc
、mxFree
:这些函数用于在MEX文件中分配和释放内存,类似于标准C库中的malloc
、calloc
、realloc
和free
。
-
错误处理:
mexErrMsgIdAndTxt
、mexWarnMsgIdAndTxt
:这些函数用于在MEX函数执行过程中显示错误或警告信息,并且可以选择性地终止MEX文件的执行。
-
MATLAB数据访问和创建:
mxCreateDoubleMatrix
、mxCreateNumericArray
、mxCreateCharArray
等:这些函数用于创建各种类型的MATLAB数据结构(如矩阵、数组等)。mxGetPr
、mxGetPi
、mxGetData
、mxGetScalar
等:这些函数用于访问MATLAB数据结构中的实际数据。mxSetPr
、mxSetPi
、mxSetData
等:这些函数用于设置MATLAB数据结构中的数据。
-
数据属性查询:
mxGetM
、mxGetN
、mxGetNumberOfElements
、mxGetClassID
等:这些函数用于查询MATLAB数组的维度、元素数量、数据类型等属性。
-
类型检查和转换:
mxIsDouble
、mxIsSingle
、mxIsClass
等:这些函数用于检查MATLAB数组的数据类型。mxGetClassID
:获取MATLAB数组的类别。
-
MEX文件入口点:
mexFunction
:所有MEX文件都必须定义这个函数,它是MEX文件的入口点,MATLAB调用MEX文件时就是通过这个函数。
-
调用MATLAB函数:
mexCallMATLAB
:允许MEX文件中的代码调用MATLAB中的函数。
三、一个小例子
3.1、装入编译器
可以看到可以选两种编译器,一种是 C++
另一种是 FORTRAN
3.2、写C函数
在同一目录下,创建一个 ADD.c
文件,其函数形式必须是
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
其中
- nlhs:输出参数个数
- plhs:输出参数列表
- nrhs:输入参数个数
- prhs:输入参数列表
保存的文件名就是将来在MATLAB中调用的函数名,而不是这里的函数名。
下面给出一个例子,其中输入是两个矩阵,返回一个矩阵,实现矩阵中元素的相加。
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
if(nrhs <1) {
mexPrintf("input param count error\n");
return;
}
int rowNum1 = mxGetM(prhs[0]); // 获得第一个矩阵的行数
int colNum1 = mxGetN(prhs[0]); // 获得第一个矩阵的列数
double* pArr2 = (double*)mxGetPr(prhs[0]); // 获得第一个矩阵的指针,这里是一维的
int rowNum2 = mxGetM(prhs[1]); // 获得第二个矩阵的行数
int colNum2 = mxGetN(prhs[1]); // 获得第二个矩阵的列数
double* pArr2 = (double*)mxGetPr(prhs[1]); // 获得第二个矩阵的指针,这里是一维的
if (rowNum1 != rowNum2 || colNum2 != colNum2) { // 保证两个矩阵同纬度
mexPrintf("two matrix is different size\n");
}
plhs[0] = mxCreateDoubleMatrix(rowNum1, colNum1, mxREAL); // 创建返回矩阵
double* retValue = mxGetPr(plhs[0]); // 创建返回矩阵的执政
for(int i=0;i<rowNum1*colNum1;i++) {
retValue[i] = pArr1[i] + pArr2[i]; // 填充数据
}
return;
}
res = ADD([1,2],[3,4])
我们编译一下,编译指令 mex ADD.c
3.3、一些问题
问:我们传入的是一个二维矩阵,为什么从C代码中确实一个一维指针指向了存储的数据?
答: 在C中处理从MATLAB传入的二维矩阵时,您通常会通过mxGetPr
函数获得一个指向矩阵第一个元素的指针。这个指针可以被视为指向一个一维数组的指针,而不是直接作为二级指针。因为MATLAB矩阵在内存中是按列主序(column-major)存储的,所以即便我们想要像操作二维数组那样操作它们,也需要一些额外的步骤来正确地索引这个数组。