下面我将从核心概念、主要方法、编程步骤、完整实例以及常见问题等多个方面,为你详细讲解C语言与MATLAB的接口编程。

核心概念:为什么需要接口?
- 性能瓶颈:MATLAB虽然易用,但在处理大规模循环、底层算法或高频数据时,其解释执行的速度可能不如C语言的编译执行快。
- 代码复用:你可能拥有大量经过验证的、高效的C/C++代码库(数学算法、硬件驱动、第三方库),不希望用MATLAB重写。
- 硬件交互:C/C++是直接与硬件(如数据采集卡、传感器)通信的首选语言。
- 独立部署:可以将最终算法打包成一个独立的C/C++可执行文件或库,脱离MATLAB环境运行。
接口编程的本质就是让C语言编写的函数可以被MATLAB程序调用,就像调用一个内置的MATLAB函数一样。
主要接口方法
MATLAB提供了几种与C/C++交互的方式,每种方式适用于不同的场景。
| 方法 | 描述 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| MEX (MATLAB Executable) | 将C/C++代码编译成MEX函数,在MATLAB中直接调用。 | 无缝集成,数据传递直接,调用方式与M函数无异。 | 编译后的文件只能在MATLAB环境中运行。 | 性能关键的算法、大型循环、需要访问MATLAB数据结构的场景。 |
| MAT Engine API | 通过C/C++程序作为“客户端”,主动连接和控制一个正在运行的MATLAB进程。 | 可以实现双向通信和数据交换,灵活性高。 | 编程复杂,需要管理MATLAB的生命周期。 | 将C/C++应用程序作为主程序,驱动MATLAB进行计算和绘图。 |
| MAT File API | 用于在C/C++程序中读写 .mat 文件。 |
简单、独立,不依赖MATLAB运行时。 | 只能进行文件I/O,无法实时调用函数。 | 数据持久化,将C/C++程序的计算结果保存为MATLAB可用的文件,或反之。 |
对于大多数“用C加速MATLAB”的需求,MEX接口是首选和最常用的方法,本文将重点讲解MEX接口。
MEX接口编程详解
工作原理
MEX接口的核心是Gateway Routine(门例程),这是一个特殊的C/C++函数,它充当了MATLAB和你的C代码之间的桥梁。

- 入口函数:MEX程序的入口函数通常名为
mexFunction,当你在MATLAB中调用MEX函数时,MATLAB会自动找到并执行这个mexFunction。 - 参数传递:
mexFunction接收两个参数:nlhs和plhs。nlhs: Number of Left-hand Side (输出参数的个数)。plhs: Pointer to Left-hand Side (指向输出参数的指针数组)。prhs: Pointer to Right-hand Side (指向输入参数的指针数组)。nrhs: Number of Right-hand Side (输入参数的个数)。
- 数据类型:MATLAB和C语言的数据类型不同,MEX API提供了一套转换函数(
mx*函数族)来在两者之间转换数据。
关键API函数
在C语言MEX程序中,你需要频繁使用MATLAB提供的 mex.h 头文件中的函数。
| C语言操作 | MEX API 函数 | 说明 |
|---|---|---|
| 创建数据 | mxArray *mxCreateDoubleMatrix(mwSize m, mwSize n, mxComplexity ComplexFlag) |
创建一个double类型的矩阵。mxCreateNumericArray等用于创建其他类型。 |
| 获取数据指针 | double *mxGetPr(const mxArray *array_ptr) |
获取实部数据指针。mxGetPi获取虚部指针。 |
| 设置数据指针 | void mxSetPr(mxArray *array_ptr, double *pr) |
设置实部数据指针。 |
| 获取维度信息 | mwSize *mxGetDimensions(const mxArray *array_ptr) |
获取一个指向维度数组的指针。mxGetNumberOfDimensions获取维度数。 |
| 获取元素总数 | mwSize mxGetNumberOfElements(const mxArray *array_ptr) |
获取矩阵中元素的总数。 |
| 销毁数据 | void mxDestroyArray(mxArray *array_ptr) |
释放mxArray占用的内存,防止内存泄漏。 |
| 错误处理 | void mexErrMsgTxt(const char *error_msg) |
打印错误信息并终止MEX函数执行。 |
void mexWarnMsgTxt(const char *warning_msg) |
打印警告信息,但继续执行。 |
MEX编程完整实例:实现一个简单的向量加法
这个例子将展示如何创建一个C语言MEX函数,它接收两个向量,返回它们的和。
步骤1:编写C源代码 (vector_add.c)
#include "mex.h"
// 必须有一个名为 mexFunction 的函数作为入口点
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
// 1. 检查输入参数数量
// nrhs 应该是 2 (两个输入向量)
if (nrhs != 2) {
mexErrMsgTxt("Two input arguments required.");
}
// nlhs 应该是 1 (一个输出向量)
if (nlhs != 1) {
mexErrMsgTxt("One output argument required.");
}
// 2. 检查输入参数类型是否为 double 向量
// 检查第一个输入参数
if (!mxIsDouble(prhs[0]) || mxIsComplex(prhs[0]) || mxGetNumberOfDimensions(prhs[0]) > 2) {
mexErrMsgTxt("Input 1 must be a real double vector or matrix.");
}
// 检查第二个输入参数
if (!mxIsDouble(prhs[1]) || mxIsComplex(prhs[1]) || mxGetNumberOfDimensions(prhs[1]) > 2) {
mexErrMsgTxt("Input 2 must be a real double vector or matrix.");
}
// 3. 获取输入数据指针和大小
double *A = mxGetPr(prhs[0]); // 获取输入向量A的数据指针
double *B = mxGetPr(prhs[1]); // 获取输入向量B的数据指针
mwSize M = mxGetM(prhs[0]); // 获取输入向量A的行数
mwSize N = mxGetN(prhs[0]); // 获取输入向量A的列数
// 为了简单起见,我们假设两个输入矩阵大小相同
// 实际应用中应该添加维度检查
if (M != mxGetM(prhs[1]) || N != mxGetN(prhs[1])) {
mexErrMsgTxt("Input matrices must have the same dimensions.");
}
// 4. 创建输出参数
// 创建一个与输入A相同大小的 double 矩阵作为输出
plhs[0] = mxCreateDoubleMatrix(M, N, mxREAL);
// 5. 获取输出数据指针
double *C = mxGetPr(plhs[0]);
// 6. 执行核心计算 (这里是简单的向量加法)
mwSize i;
for (i = 0; i < M * N; i++) {
C[i] = A[i] + B[i];
}
// 注意:mxArray 对象由MATLAB管理,当MEX函数执行完毕后,
// plhs[0] 所指向的内存会自动被MATLAB释放,所以不需要我们手动 mxDestroyArray。
}
步骤2:编译MEX文件
在MATLAB命令窗口中,使用 mex 命令进行编译。
% 确保当前目录是你的 .c 文件所在目录 % mex vector_add.c
- 在Windows上:
mex命令会自动调用Visual C++ (MSVC) 编译器,如果遇到问题,可能需要运行mex -setup来配置编译器。 - 在Linux/macOS上:
mex默认会调用GCC或Clang编译器,同样,如果遇到问题,运行mex -setup进行配置。
编译成功后,会在当前目录下生成一个名为 vector_add.mexw64 (Windows) 或 vector_add.mexa64 (Linux) 的文件。

步骤3:在MATLAB中调用MEX函数
你可以像调用普通M函数一样调用它了。
% 定义两个输入向量
A = [1, 2, 3; 4, 5, 6];
B = [0.5, 0.5, 0.5; 0.5, 0.5, 0.5];
% 调用我们刚创建的MEX函数
C = vector_add(A, B);
% 显示结果
disp('Input A:');
disp(A);
disp('Input B:');
disp(B);
disp('Output C (from MEX):');
disp(C);
% 为了验证,我们用MATLAB内置函数计算
C_matlab = A + B;
disp('Output C (from MATLAB):');
disp(C_matlab);
% 比较结果
if isequal(C, C_matlab)
disp('Results are identical!');
else
disp('Results differ!');
end
输出结果:
Input A:
1 2 3
4 5 6
Input B:
0.5000 0.5000 0.5000
0.5000 0.5000 0.5000
Output C (from MEX):
1.5000 2.5000 3.5000
4.5000 5.5000 6.5000
Output C (from MATLAB):
1.5000 2.5000 3.5000
4.5000 5.5000 6.5000
Results are identical!
更复杂的实例:处理字符串和结构体
MEX API同样支持处理更复杂的数据类型,如字符串、结构体、单元数组等。
示例:创建一个包含字符串和数值字段的结构体
// create_struct.c
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
if (nlhs != 1) {
mexErrMsgTxt("One output argument required.");
}
// 1. 创建一个 1x1 的结构体数组
plhs[0] = mxCreateStructMatrix(1, 1, 2, (const char *[]){"name", "age"});
// 2. 为 "name" 字段创建一个字符串 mxArray
mxArray *name_field = mxCreateString("John Doe");
// 将 name_field 设置为结构体的 "name" 字段
mxSetField(plhs[0], 0, "name", name_field);
// 3. 为 "age" 字段创建一个数值 mxArray
mxArray *age_field = mxCreateDoubleScalar(30);
// 将 age_field 设置为结构体的 "age" 字段
mxSetField(plhs[0], 0, "age", age_field);
// 注意:mxSetField 会获取 name_field 和 age_field 的所有权,
// 所以我们不需要在这里调用 mxDestroyArray。
}
编译与调用:
mex create_struct.c person = create_struct(); disp(person); disp(person.name); disp(person.age);
输出结果:
name: 'John Doe'
age: 30
John Doe
30
常见问题与最佳实践
-
内存泄漏:这是MEX编程中最常见的问题。
- 规则:任何通过
mxCreate*函数创建的mxArray,都必须在适当的时候通过mxDestroyArray释放,除非它已经被mxSetField或mxSetCell等函数转移了所有权。 - 检查:使用MATLAB的内存分析器(在“主页”选项卡下 -> “代码分析” -> “运行代码分析器”)可以帮助发现潜在的内存泄漏。
- 规则:任何通过
-
输入参数检查:永远不要假设输入参数的格式、类型或大小,始终在函数开头使用
mxIs*系列函数(如mxIsDouble,mxIsChar)和mxGet*系列函数进行严格检查,否则你的程序很容易崩溃。 -
线程安全:MEX函数默认不是线程安全的,如果你想在多线程环境中调用MEX函数,必须使用
mexLock和mexUnlock来管理,或者确保你的代码是线程无关的。 -
性能分析:使用MATLAB的 Profiler 工具来分析你的M函数,如果发现某个函数是性能瓶颈,再考虑将其用C语言重写为MEX函数,不要过早优化。
-
编译器兼容性:确保你的C/C++代码符合你所选编译器的标准,避免使用编译器特有的、非标准的扩展,以保证代码的可移植性。
| 接口方式 | 核心思想 | 适用场景 |
|---|---|---|
| MEX | C代码作为MATLAB函数的扩展 | 加速计算、复用现有C代码库、底层操作。 |
| Engine API | C程序控制MATLAB进程 | 将MATLAB作为计算引擎嵌入到C/C++应用程序中。 |
| MAT File API | C程序读写MAT数据文件 | 数据持久化、数据交换。 |
对于绝大多数希望通过C语言提升MATLAB程序性能的场景,MEX接口是标准且最强大的解决方案,掌握它,意味着你可以在MATLAB生态和底层高性能世界之间自由切换,构建出既强大又高效的混合系统。
