
简介MATLAB Support for MinGW-w64 C/C/Fortran Compiler 支持包为 MATLAB 2023B/2024A 用户提供 MinGW-w64 工具链解决在 MATLAB 环境中编译 C/C/Fortran MEX 文件与外部程序的痛点。该包常与 TrueTime 2.0 实时系统工具箱配合使用适合需要在 Simulink 中仿真嵌入式实时行为、或希望用编译型语言加速计算任务的工程师与研究人员。压缩包共 2000 个文件约 133.84MB主要由 787 个头文件、243 个 hpp 头文件、896 个 Python 脚本构成另有少量 C 源码、Shell 脚本与文本说明头文件与库文件支撑编译链接脚本与配置辅助环境整合目录中 include、lib、bin、licenses 等模块区分清晰便于按需调用。目前已有 1912 人学习下载说明该支持包在相关社群中具备实用参考价值。借助该包可快速配置 MATLAB 与 MinGW-w64 的交叉编译链路省去手动下载与路径配置的繁琐流程还可在 TrueTime 2.0 场景下编译实时仿真组件直接提升计算与仿真效率。1. 这支持包解决的是“装不上 C/C 编译器”的痛点写算法的人多半撞过这面墙手里的 C/C 代码想编译成 MEX 文件可机器上既没装 Visual Studio又怕装完把系统环境搅乱。MATLAB Support for MinGW-w64 C/C/Fortran Compiler 就是为这种场景准备的它是 MATLAB 官方发布的附加功能把 MinGW-w64 工具链直接放进 MATLAB 安装目录让mex命令能在不碰系统全局环境的情况下完成 C、C、Fortran 三种语言的编译。安装之后mex -setup会自动识别出编译器剩下的工作就是写好源码、敲一条编译命令。这篇文章会从安装讲到避坑并给出可以直接复制的三种语言 MEX 示例适合手头有算法要移植、又不想在 Visual Studio 上折腾的从业者。2. 安装与配置从附加功能到mex -setup的完整过程2.1 为什么 MATLAB 要单独提供 MinGW-w64 支持包在 Windows 上编译 MEX 文件本质上是调用一个外部 C/C 编译器把源码变成遵循 MEX ABI 的 DLL。MATLAB 默认首选 Visual Studio但 Visual Studio 体积大、版本与系统镜像绑定很多只做算法验证的机器根本没有装。MinGW-w64 是 Windows 下的 GCC 工具链包含gcc、g、gfortran和配套的运行库它体积可控不需要管理员重装系统组件。MATLAB 官方将整个工具链打包成支持包直接安装在 MATLAB 安装目录下这样不会与你自己安装的其他 MinGW 版本冲突也能保证和 MATLAB 内部的运行时库版本对齐。这个支持包解决的实际问题有两个第一让mex命令有一个干净的编译器来源不需要手动到 MinGW-w64 官网下载一堆压缩包再配 PATH第二让 Fortran 源码也能进入 MATLAB 的编译体系。传统方案里Fortran MEX 需要你自己装 gfortran 并手工配置mexopts文件坑多且易碎。官方支持包把这一步做成了向导式安装省掉大量初始化成本。2.2 安装支持包的两种途径最常见的路径是在 MATLAB 图形界面里操作。打开 MATLAB 后在主页选项卡右侧找到“附加功能”下拉菜单中选择“获取附加功能”会弹出 Add-On Explorer 窗口。在搜索框输入“MinGW-w64”结果第一项一般是“MATLAB Support for MinGW-w64 C/C/Fortran Compiler”点击“安装”。MATLAB 会要求你登录账户随后开始下载并自动安装到当前 MATLAB 版本。安装完成后建议关闭并重新启动 MATLAB因为mex命令的配置缓存是在启动时生成的不重启可能看不到新编译器。如果目标机器没有外网或者你想在多台机器上统一版本推荐离线安装方式。在联网机器上用附加功能管理器先执行一次安装下载过程会被同时缓存到本地的账户文件夹中。你也可以从账户的“我的附加功能”页面选择对应 MATLAB 版本的离线包得到一个.mlpkginstall文件。把它拷到目标机器上双击MATLAB 会弹出安装向导选择要安装到的 MATLAB 版本后等待完成即可。离线安装的版本必须和目标 MATLAB 大版本一致比如你用的是 2023a就选 2023a 的包跨版本经常会导致编译配置识别失败。2.3 用mex -setup让 MATLAB 认到编译器安装完成后在 MATLAB 命令窗口运行mex -setup会看到类似下面的输出mex -setup输出可能包含MEX configured to use MinGW64 C/C Compiler (64-bit) for C and C language compilation.如果你的机器同时装有 Visual Studiomex -setup默认可能选中 Visual Studio。这时可以直接指定语言mex -setup C mex -setup C如果 Fortran 编译器也正常工作再执行mex -setup Fortran这里最后一个命令会显示MinGW64 Fortran Compiler (64-bit)并且把mexopts指向gfortran。需要说明的是mex -setup Fortran这条命令只在支持包包含 Fortran 时才有效如果安装的是只支持 C/C 的老版本支持包会提示找不到对应编译器。2.4 确认安装结果的好用命令mex命令自带编译器探测接口比肉眼看输出更可靠。运行configs mex.getCompilerConfigurations(C, Installed)返回结果是一个结构体数组每个元素包含Name、Manufacturer、Version、Location等字段。其中Location指向matlabroot\bin\win64\mingw64下的编译器路径看到它基本可以确认 C 编译器已就位。再看 Fortranconfigs mex.getCompilerConfigurations(Fortran, Installed)如果返回空数组说明支持包的 Fortran 部分没有安装成功。还有个常用检查是看配置文件是否生成运行dir(fullfile(prefdir, *mex*))如果能看到mex_C_win64.xml或mex_F_win64.xml说明 MATLAB 已经为对应语言写好了编译规则。这两个 XML 文件是后续排查的关键第 5 章会用到它们。[status, cmdout] system(fullfile(matlabroot, bin, win64, mingw64, bin, gcc, --version))status为 0 表示命令成功cmdout会显示 GCC 版本。这样可以精确知道编译器的版本号便于后面判断 C 标准支持情况。如果之后想切换回 Visual Studio重新运行mex -setup即可。不要手动去删支持包目录正确做法是在附加功能管理器里找到已安装的支持包点卸载。手动删目录会导致 MATLAB 启动时残留mexopts引用反而更麻烦。提示安装支持包后必须重启 MATLAB 一次否则mexopts缓存可能还停留在旧状态。3. 用 C/C 编译 MEX从最小例子到多文件链接3.1 一个能跑通的 C MEX 最小示例我们把源码保存在doubleit.c。这里的逻辑很直接读取输入数组的所有元素每个元素乘以 2写回输出数组。#include mex.h void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { double *in, *out; mwSize n; mwIndex i; if (nrhs ! 1) { mexErrMsgIdAndTxt(MyMex:doubleit:nrhs, 需要一个输入参数); } if (!mxIsDouble(prhs[0])) { mexErrMsgIdAndTxt(MyMex:doubleit:type, 输入必须是 double 数组); } n mxGetNumberOfElements(prhs[0]); plhs[0] mxCreateNumericMatrix(1, n, mxDOUBLE_CLASS, mxREAL); in mxGetPr(prhs[0]); out mxGetPr(plhs[0]); for (i 0; i n; i) { out[i] in[i] * 2.0; } }这段代码里mexFunction是 MEX 文件与 MATLAB 的唯一入口四个参数分别是输出数量、输出指针数组、输入数量、输入指针数组。mxGetNumberOfElements返回数组元素个数类型为mwSize在 64 位 Windows 下是 64 位无符号整数。mxCreateNumericMatrix创建 1 行 n 列的输出矩阵这里把输出弄成了行向量如果希望保持输入维度可以改成mxCreateNumericArray或者用mxDuplicateArray再直接赋值但这里只演示最小逻辑。mxGetPr返回指向 double 数据的指针C/C 侧可以直接像普通数组一样访问。编译命令mex -R2018a doubleit.c-R2018a指定 MEX 接口版本。从 R2018a 开始MATLAB 对 MEX 的 API 做了一些调整使用这个标志可以让编译出的 MEX 在后续版本中保持兼容。如果省略编译器会使用当前 MATLAB 默认接口效果通常也一样。编译完成后当前目录会出现doubleit.mexw64文件这是 64 位 Windows 下的 MEX 专用 DLL 扩展名。现在调用验证doubleit([1 2 3; 4 5 6])因为函数内部把输入展平成了 1 行 n 列所以结果是一个行向量[2 4 6 8 10 12]而不是保持原来的 2 行 3 列。这是最小示例特意简化实际项目中应加一行代码记录输入维度并传递给输出矩阵。3.2 扩展成 C 并加入标准库当代码量变大用 C 可以利用容器和算法。下面这个版本用std::vector作为中间缓冲再用 lambda 做乘法#include mex.h #include vector #include algorithm void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { if (nrhs ! 1) { mexErrMsgIdAndTxt(MyMex:doubleit:nrhs, 需要一个输入参数); } double *in mxGetPr(prhs[0]); mwSize n mxGetNumberOfElements(prhs[0]); std::vectordouble buf(in, in n); std::transform(buf.begin(), buf.end(), buf.begin(), [](double v) { return v * 2.0; }); plhs[0] mxCreateNumericMatrix(1, n, mxDOUBLE_CLASS, mxREAL); double *out mxGetPr(plhs[0]); std::copy(buf.begin(), buf.end(), out); }这段代码的核心是std::vectordouble buf(in, in n)用输入数组的指针区间直接构造容器省掉手工for拷贝。std::transform对每个元素执行 lambdalambda 在这里就是“乘以 2”的匿名函数。最后再用std::copy把处理结果拷回 MATLAB 分配的输出缓冲区。编译 C 源码要注意编译器标准。MATLAB 自带的 MinGW-w64 版本如果偏旧默认可能使用 C98 标准而 lambda 要到 C11 才有。所以我会建议编译时显式指定标准mex -stdc11 -R2018a doubleit.cpp-stdc11会原样透传给 g。如果还用到了std::make_unique这类 C14 特性就换成-stdc14。如果看到编译器报错command line option --std... is valid那说明mex没有把这个选项传给编译器此时需要检查mexopts文件里的CXXFLAGS是否包含$CXXFLAGS变量否则要手动改配置。3.3 连接外部库时的参数写法MEX 工程一旦涉及信号处理、图像算法基本都要链接第三方库。mex命令的链接参数与 gcc/g 几乎一致常用的是-I、-L和-l。比如我要链接当前目录include下的头文件和lib目录下的fftw3库mex -R2018a myfilter.cpp -I./include -L./lib -lfftw3 -lm其中-I./include告诉编译器去哪找fftw3.h-L./lib告诉链接器去哪找libfftw3.a或fftw3.dll-lfftw3表示链接库名 fftw3。MinGW 会自动补全文件名前缀lib和扩展名.a/.dll。如果没有找到库文件mex的最后一步会出现类似ld: cannot find -lfftw3的错误此时先确认lib目录下文件名是不是libfftw3.a如果库名是fftw3-3.dll这种带版本号的动态库就需要用-l:libfftw3-3.dll这种精确指定写法但这是少数情况。一个容易忽略的点是MinGW 的-l解析的是 GCC 风格的库而 Visual Studio 编译生成的.lib文件通常不能直接用。两者使用的符号命名和链接约定不同强行链接会报大量undefined reference。如果第三方库只提供 MSVC 版本常见做法是换成动态链接方式在 C 源码里用LoadLibrary和GetProcAddress手动加载或者在安装库时选择 MinGW 版本。我在实际项目里遇到过一次针对同一算法的不同库MSVC 版能编过但运行崩溃换成 MinGW 版才稳定。3.4 在 MATLAB 里验证结果编译唤醒后不要直接上线先在 MATLAB 里做正确性校验。最简单的做法是对比 MEX 输出和纯 MATLAB 计算结果x rand(1, 10); y1 doubleit(x); y2 x * 2; assert(isequal(y1, y2));这里isequal会比较数值类型和值MEX 返回值类型是 double纯 MATLAB 的x*2也是 double所以可以放心比对。如果函数包含浮点运算直接用isequal可能因为舍入误差失败这时改用max(abs(y1 - y2)) 1e-12接下来才是性能验证。不要把timeit(() x*2)和 MEX 比因为 MATLAB 对这类简单向量运算有极强的内置向量化能力比 MEX 还快是正常的。正确的做法是挑一段真正需要逐点处理的算法把 MATLAB 循环版本和 MEX 版本放在一起测N 5000; data randn(N); t timeit(() doubleit(data)); tl timeit(() 2*data);只有当算法复杂到 MATLAB 循环会拖慢场景时MEX 的收益才明显。这个验证习惯能避免你把时间花在优化一个已经足够快的函数上。4. 用 Fortran 编译 MEX数组顺序与符号别踩坑4.1 Fortran MEX 的接口规则MATLAB 官方 MEX 文档对 C/C 和 Fortran 都支持但使用者数量差一个量级。Fortran MEX 的入口子程序仍然叫mexFunction参数顺序与 C 一致但类型必须用fintrf.h中定义的mwPointer、mwSize。在 64 位 Windows 下这些类型最终会对应 64 位整数。Fortran 的数组存储是列优先column-majorMATLAB 也是列优先这点比让 C 代码揉到 MATLAB 里更自然。但 Fortran 的符号名和参数传递默认是按引用传递GPU 相关 API 没有全套封装所以你还是会遇到一些特有的边界问题。使用mex -setup Fortran之后mex命令会根据源文件的扩展名.f或.F调用 gfortran。MATLAB 支持包提供的 gfortran 和 MinGW-w64 的 GCC 版本配套因此 C/C 与 Fortran 混编时只要都用这套工具链链接时不会出现 ABI 冲突。更老的做法是手动安装 gfortran 并自己写mexopts文件现在支持包把这个环节省掉了。4.2 完整可编译的 gfortran MEX 示例下面是一个最小但完整的 Fortran MEX功能与前面的 C 版本相同输入一个 double 标量输出它的两倍。文件保存为doubleit_f.f#include fintrf.h subroutine mexFunction(nlhs, plhs, nrhs, prhs) implicit none integer(8) :: nlhs, nrhs integer(8) :: plhs(*), prhs(*) integer(8) :: x_ptr, y_ptr double precision :: x, y if (nrhs / 1) then call mexErrMsgIdAndTxt(MyMex:doubleit_f:nrhs, 需要一个输入参数) end if x 0.0d0 x_ptr mxGetPr(prhs(1)) call mxCopyPtrToReal8(x_ptr, x, 1) y x * 2.0d0 plhs(1) mxCreateDoubleMatrix(1_8, 1_8, 0_8) y_ptr mxGetPr(plhs(1)) call mxCopyReal8ToPtr(y, y_ptr, 1) end这段 Fortran 代码用了固定格式第 1 列写注释或续行标志前 6 列是标号区代码从第 7 列开始。integer(8)是 gfortran 下对 64 位整型的写法MATLAB 的指针在 64 位 Windows 下正好是 64 位所以我统一用integer(8)来声明mexFunction的四个参数避免 32 位integer截断指针导致崩溃。mxCopyPtrToReal8把 MATLAB 侧 double 数据从指针x_ptr拷贝到 Fortran 变量x最后一个参数是拷贝的元素个数标量用 1。mxCreateDoubleMatrix(1_8, 1_8, 0_8)创建 1×1 的实数 double 矩阵第三个参数0_8对应mxREAL。编译命令mex -R2018a doubleit_f.f如果当前目录没有生成.mexw64先确认mex -setup Fortran已经选对编译器。编译通过后调用doubleit_f(21)正确结果是 42。4.3 Fortran 数组维度与指针拷贝的参数陷阱上面的标量示例安全但实际项目里传数组时坑很多。首先mxGetNumberOfElements返回的是元素数量这个值传给mxCopyPtrToReal8时必须匹配 Fortran 接收数组的维度。假设输入是 100 个元素的行向量你把它对应到 Fortran 的real(8) :: x(100)那么mxCopyPtrToReal8(x_ptr, x, 100)没问题。但如果你写成real(8) :: x(1)编译可能还过运行时内存越界会把 MATLAB 进程打崩。其次mxCopyReal8ToPtr的第二个参数是目标指针这里的指针是integer(8)不能直接传入一个普通整数变量。我看到过有人把指针当成real*8传结果写入的地址完全错位。正确写法是先从mxGetPr(plhs(1))取出y_ptr再传给拷贝子程序。第三个参数同样是元素个数不是字节数。对于二维数组Fortran 和 MATLAB 都是列优先所以维度顺序可以直接对齐。但要注意 Fortran 侧声明的形状必须和mex返回的维度匹配。如果你在 MATLAB 侧用mxGetM和mxGetN拿行数和列数传给 Fortran 后建议在 Fortran 端把它们声明为integer(8)再动态分配二维数组。固定形状数组有时会因为起始下标从 1 开始而错位所以实际项目我会用可分配数组。4.4 什么时候才需要 Fortran MEX我的经验是只有当手头有一套稳定且经过验证的 Fortran 数值子程序时才值得走 Fortran MEX 这条路。比如某些老牌求根、优化算法原始代码是 Fortran 77逐行移植成 C 代价高动用 gfortran 直接编译成 MEX 是最短路径。如果是从零开始写新算法我更推荐先用 C/C因为 MATLAB 的 MEX 文档和第三方资料绝大多数围绕 C/C遇到问题能搜到更多现成方案。此外MinGW-w64 的 gfortran 还能帮助编译一些 Fortran 写的外部库生成静态库再通过 C 语言 MEX 去链接。不过这会引入混编复杂度需要在 Fortran 端用iso_c_binding包一层接口。如果你只是想让 MATLAB 调用 Fortran 生成的 DLL比写 MEX 更快的路是loadlibrary但那属于另一个话题了。5. 避坑与常见问题现象、原因、解决5.1 安装了支持包但mex -setup不显示 MinGW-w64现象支持包安装过程没有任何报错重启 MATLAB 后执行mex -setup输出仍然显示没有找到编译器或者只罗列 Visual Studio。原因最常见是支持包装进了另一个 MATLAB 版本。Windows 上如果同时装过两个不同版本的 MATLAB离线包默认可能覆盖到当前最后打开的版本之外。其次是prefdir目录下的mexopts缓存没有生成或者生成失败常见于用户文件夹没有写权限。解决先用mex.getCompilerConfigurations(C, Installed)确认 MATLAB 是否知道自己有编译器返回空的时候去[matlabroot /bin/win64/mingw64/bin]下检查gcc.exe是否存在。如果编译器文件在但 MATLAB 不认识删除prefdir下的mexopts相关 XML 文件再重启 MATLAB让 MATLAB 重新扫描一次。注意prefdir的位置每个人不同用这个命令打印prefdir如果还是不行手动执行mex -setup -v看看扫描过程卡在哪一行。5.2 MEX 运行时报缺 DLLlibgcc_s_seh-1.dll 或 libstdc-6.dll现象mex编译顺利但在 MATLAB 里调用函数时报错“无法定位程序输入点”或“找不到 libgcc_s_seh-1.dll”。原因MinGW-w64 的 C/C 运行时库默认是动态链接的。MEX 文件被 MATLAB 加载时系统会到 PATH 里找这些 DLL但支持包并没有把mingw64\bin加进 PATH导致加载失败。解决有两种选择。第一种是把支持包的 bin 目录加入系统 PATH一劳永逸但会改变系统环境如果机器上还有另一个 MinGW 版本容易引发冲突。第二种更推荐启动 MATLAB 后把需要的 DLL 复制到 MEX 文件同一目录dest fileparts(which(doubleit)); if isempty(dest), dest pwd; end copyfile(fullfile(matlabroot, bin, win64, mingw64, bin, libstdc-6.dll), dest); copyfile(fullfile(matlabroot, bin, win64, mingw64, bin, libgcc_s_seh-1.dll), dest);这样 DLL 和 MEX 文件放在一起Windows 加载器会优先去 MEX 文件所在目录找依赖。注意如果matlabroot路径含空格copyfile的两个参数要写成带引号的字符串上面代码已经能正常处理。如果你编译的是 Fortran MEX通常还会需要libgfortran-5.dll或类似文件同样复制到同目录。5.3 C 标准版本太旧lambda 和高级特性编译不过现象源码在本地其他编译器能通过但在 MATLAB 里用mex编译时报lambda expressions only available with -stdc11或者static_assert等新特性不可用。原因支持包携带的 GCC 版本未开启新标准或者mexopts文件里CXXFLAGS不含-stdc11。MinGW-w64 支持包从某个 MATLAB 版本开始默认使用-stdc14但如果你装的是旧版本或离线包版本不新默认可能还是-stdgnu98。解决直接在mex命令里追加标准参数mex -stdc17 -R2018a doubleit.cpp如果mex提示无法识别-stdc17说明支持的 GCC 版本太老需要升级支持包而不是硬传参数。也可以打开编译配置 XML找到CXXFLAGS字段把-stdc14加进去之后所有.cpp文件都会用这个标准。但要留意如果工程里同时链接了用更高标准编译的第三方库标准库 ABI 不同会引入链接错误这时所有参与编译的源文件最好统一标准。5.4 Fortran MEX 调用崩溃八成是类型宽度和符号问题现象Fortran MEX 编译通过但一调用 MATLAB 就崩溃或者输出结果全是 0。有的还会出现Invalid MEX-file提示mexFunction没有被找到。原因第一指针或长度变量没有用 64 位整数声明。在 64 位 Windows 下普通integer是 32 位指针是 64 位把mxGetPr的结果放进 32 位变量会直接截断访问必然崩溃。第二gfortran 的符号名会带下划线MATLAB 期望找到小写mexfunction两者通常能对上但如果源文件里写了MODULE或加了类似extern C的机制干扰符号就可能导致找不到入口。解决统一使用integer(8)。在mexFunction的参数和所有指针变量上都加integer(8)包括nlhs、nrhs。另外源文件首行必须是#include fintrf.h并且不能把它放到子程序内部。检查符号可以用 MinGW 的nm工具nm doubleit_f.mexw64 | grep -i mexfunction如果看到mexfunction_说明符号正常。如果什么都没有检查代码里mexFunction拼写和大小写。实际还有一种情况是 Fortran 源文件用了自由格式但保存为.f扩展名gfortran 会强制按固定格式读把后面的代码全当成注释导致编译出来只有空壳子程序调用时会报符号找不到。解决这类问题最简单的方法是让文件扩展名明确匹配源码格式固定格式用.f自由格式用.f90。6. 用mex -v和build.m把编译流程固定下来当你同时要维护十几个 MEX 文件或者每周换一次开发机每次手敲mex命令会很快失去耐心。我的习惯是给每个工程写一个build.m把所有编译选项统一放进去。function build() opts -R2018a -O -stdc14; mex(opts, src/demo.cpp, src/utils.cpp, -Iinclude, ... -Llib, -lengine); fprintf(编译完成请运行测试脚本\n); end这里opts是公共参数mex接受一个字符串数组的多个片段后面的源文件和链接参数会按顺序拼成一条完整命令。写好后每次到了新机器先安装支持包再运行build项目就能重新站起来。遇到问题时mex -v是排错的第一工具。它会打印出具体的 gcc 命令行包括头文件路径、宏定义、链接顺序。看这个输出能发现很多隐形问题某个-I路径写错了、库文件顺序不对、编译器版本和你预想的不同。以前我在一个新分支上编译通过但运行崩溃用mex -v一看才发现编译器路径指向了旧版支持包目录版本混用导致 ABI 不一致改回统一路径后问题立刻消失。我现在的流程是支持包一装好先跑一次mex -setup -v再跑mex.getCompilerConfigurations把编译器版本记到工程说明里。之后再写build.m最后才写源码。这个顺序看着反直觉但它能避免你先写完一大堆 C 才发现编译器底座不对。这套习惯帮我少走不少弯路希望帮到你。本文还有配套的精品资源点击获取